|
| 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" |
0 commit comments