Skip to content

Commit eaf8011

Browse files
committed
Add approach: validate first, then transcribe
1 parent 41c53b3 commit eaf8011

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"introduction": {
3+
"authors": [
4+
"MatthijsBlom"
5+
]
6+
},
7+
"approaches": [
8+
{
9+
"uuid": "209cd027-6f98-47ac-a77f-8a083e0cd100",
10+
"slug": "validate-first",
11+
"title": "Validate first, then transcribe",
12+
"blurb": "First, find out whether there are invalid characters in the input. Then, if there aren't, transcribe the strand in one go.",
13+
"authors": [
14+
"MatthijsBlom"
15+
]
16+
}
17+
]
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Validate first, then transcribe
2+
3+
```haskell
4+
toRNA :: String -> Either Char String
5+
toRNA dna =
6+
case find (`notElem` "GCTA") dna of
7+
Nothing -> Right (map transcribe dna)
8+
Just c -> Left c
9+
where
10+
transcribe = \case
11+
'G' -> 'C'
12+
'C' -> 'G'
13+
'T' -> 'A'
14+
'A' -> 'U'
15+
```
16+
17+
One approach to solving this problem is to
18+
19+
- first check that all input characters are valid,
20+
- return one of the invalid characters if there are any, and otherwise to
21+
- convert all the DNA nucleotides into RNA nucleotides.
22+
23+
Some submitted solutions retrieve the invalid character (if present) in two steps:
24+
25+
- first check that there are _some_ invalid characters, for example using `any`, and
26+
- then find the first one, for example using `filter` and `head`.
27+
28+
The solution highlighted here combines these steps into one.
29+
As used here, `find` returns `Nothing` if there are no invalid characters, and if there are then it returns `Just` the first one.
30+
By pattern matching on `find`'s result it is determined how to proceed.
31+
32+
For transcribing DNA nucleobases into RNA nucleobases a locally defined function `transcribe` is used.
33+
It is a [partial function][wiki-partial-functions]: when given any character other than `'G'`, `'C'`, `'T'`, or `'A'` it will crash.
34+
35+
Partial functions display behavior (e.g. crashing) that is not documented in their types.
36+
This tends to make reasoning about code that uses them more difficult.
37+
For this reason, partial functions are generally to be avoided.
38+
39+
Partiality is less objectionable in local functions than in global ones, because in local contexts it is easier to make sure that functions are never applied to problematic arguments.
40+
Indeed, in the solution highlighted above it is clear that `transcribe` will never be applied to a problematic character, as if there were any such characters in `dna` then `find` would have returned `Just _` and not `Nothing`.
41+
42+
Still, it would be nice if it weren't necessary to check that `transcribe` is never applied to invalid characters.
43+
`transcribe` is forced by its `Char -> Char` type to either be partial or else to return bogus values for some inputs – which would be similarly undesirable.
44+
But another type, such as `Char -> Maybe Char`, would allow `transcribe` to be total.
45+
The other approaches use such a variant.
46+
47+
This approach has the input walked twice (or thrice).
48+
It is possible to solve this problem by walking the input only once.
49+
The other approaches illustrate how.
50+
51+
52+
[wiki-partial-functions]:
53+
https://wiki.haskell.org/Partial_functions
54+
"Haskell Wiki: Partial functions"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
toRNA :: String -> Either Char String
2+
toRNA dna =
3+
case find (`notElem` "GCTA") dna of
4+
Nothing -> Right (map transcribe dna)
5+
Just c -> Left c
6+
where
7+
transcribe = \case
8+
'G' -> 'C'

0 commit comments

Comments
 (0)