copy-edit viget posts
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
title: "OTP: a Functional Approach (or Three)"
|
||||
date: 2015-01-29T00:00:00+00:00
|
||||
draft: false
|
||||
needs_review: true
|
||||
canonical_url: https://www.viget.com/articles/otp-ocaml-haskell-elixir/
|
||||
---
|
||||
|
||||
@@ -16,20 +15,22 @@ programs the same so that I could easily see their similarities and
|
||||
differences. Check out the `encrypt` program in
|
||||
[all](https://github.com/vigetlabs/otp/blob/master/languages/OCaml/encrypt.ml)
|
||||
[three](https://github.com/vigetlabs/otp/blob/master/languages/Haskell/encrypt.hs)
|
||||
[languages](https://github.com/vigetlabs/otp/blob/master/languages/Elixir/encrypt)
|
||||
[languages](https://github.com/vigetlabs/otp/blob/master/languages/Elixir/apps/encrypt/lib/encrypt.ex)
|
||||
and then I'll share some of my favorite parts. Go ahead, I'll wait.
|
||||
|
||||
## Don't Cross the Streams {#dontcrossthestreams}
|
||||
## Don't Cross the Streams
|
||||
|
||||
One tricky part of the OTP challenge is that you have to cycle over the
|
||||
key if it's shorter than the plaintext. My initial approaches involved
|
||||
passing around an offset and using the modulo operator, [like
|
||||
this](https://github.com/vigetlabs/otp/blob/6d607129f78ccafa9a294ca04da9e4c8bf7b7cc1/decrypt.ml#L11-L14):
|
||||
|
||||
let get_mask key index =
|
||||
let c1 = List.nth key (index mod (List.length key))
|
||||
and c2 = List.nth key ((index + 1) mod (List.length key)) in
|
||||
int_from_hex_chars c1 c2
|
||||
```ocaml
|
||||
let get_mask key index =
|
||||
let c1 = List.nth key (index mod (List.length key))
|
||||
and c2 = List.nth key ((index + 1) mod (List.length key)) in
|
||||
int_from_hex_chars c1 c2
|
||||
```
|
||||
|
||||
Pretty gross, huh? Fortunately, both
|
||||
[Haskell](http://hackage.haskell.org/package/base-4.7.0.2/docs/Prelude.html#v:cycle)
|
||||
@@ -41,35 +42,42 @@ the [Batteries](http://batteries.forge.ocamlcore.org/) library) has the
|
||||
(doubly-linked list) data structure. The OCaml code above becomes
|
||||
simply:
|
||||
|
||||
let get_mask key =
|
||||
let c1 = Dllist.get key
|
||||
and c2 = Dllist.get (Dllist.next key) in
|
||||
int_of_hex_chars c1 c2
|
||||
|
||||
```ocaml
|
||||
let get_mask key =
|
||||
let c1 = Dllist.get key
|
||||
and c2 = Dllist.get (Dllist.next key) in
|
||||
int_of_hex_chars c1 c2
|
||||
```
|
||||
|
||||
No more passing around indexes or using `mod` to stay within the bounds
|
||||
of the array -- the Dllist handles that for us.
|
||||
|
||||
Similarly, a naïve Elixir approach:
|
||||
|
||||
def get_mask(key, index) do
|
||||
c1 = Enum.at(key, rem(index, length(key)))
|
||||
c2 = Enum.at(key, rem(index + 1, length(key)))
|
||||
int_of_hex_chars(c1, c2)
|
||||
end
|
||||
```elixir
|
||||
def get_mask(key, index) do
|
||||
c1 = Enum.at(key, rem(index, length(key)))
|
||||
c2 = Enum.at(key, rem(index + 1, length(key)))
|
||||
int_of_hex_chars(c1, c2)
|
||||
end
|
||||
```
|
||||
|
||||
And with streams activated:
|
||||
|
||||
def get_mask(key) do
|
||||
Enum.take(key, 2) |> int_of_hex_chars
|
||||
end
|
||||
```elixir
|
||||
def get_mask(key) do
|
||||
Enum.take(key, 2) |> int_of_hex_chars
|
||||
end
|
||||
```
|
||||
|
||||
Check out the source code
|
||||
([OCaml](https://github.com/vigetlabs/otp/blob/master/languages/OCaml/encrypt.ml),
|
||||
[Haskell](https://github.com/vigetlabs/otp/blob/master/languages/Haskell/encrypt.hs),
|
||||
[Elixir](https://github.com/vigetlabs/otp/blob/master/languages/Elixir/encrypt))
|
||||
[Elixir](https://github.com/vigetlabs/otp/blob/master/languages/Elixir/apps/encrypt/lib/encrypt.ex))
|
||||
to get a better sense of cyclical data structures in action.
|
||||
|
||||
## Partial Function Application {#partialfunctionapplication}
|
||||
## Partial Function Application
|
||||
|
||||
Most programming languages have a clear distinction between function
|
||||
arguments (input) and return values (output). The line is less clear in
|
||||
@@ -77,16 +85,20 @@ arguments (input) and return values (output). The line is less clear in
|
||||
languages like Haskell and OCaml. Check this out (from Haskell's `ghci`
|
||||
interactive shell):
|
||||
|
||||
Prelude> let add x y = x + y
|
||||
Prelude> add 5 7
|
||||
12
|
||||
```
|
||||
Prelude> let add x y = x + y
|
||||
Prelude> add 5 7
|
||||
12
|
||||
```
|
||||
|
||||
We create a function, `add`, that (seemingly) takes two arguments and
|
||||
returns their sum.
|
||||
|
||||
Prelude> let add5 = add 5
|
||||
Prelude> add5 7
|
||||
12
|
||||
```
|
||||
Prelude> let add5 = add 5
|
||||
Prelude> add5 7
|
||||
12
|
||||
```
|
||||
|
||||
But what's this? Using our existing `add` function, we've created
|
||||
another function, `add5`, that takes a single argument and adds five to
|
||||
@@ -97,8 +109,10 @@ argument and adds it to the argument passed to the initial function.
|
||||
When you inspect the type of `add`, you can see this lack of distinction
|
||||
between input and output:
|
||||
|
||||
Prelude> :type add
|
||||
add :: Num a => a -> a -> a
|
||||
```
|
||||
Prelude> :type add
|
||||
add :: Num a => a -> a -> a
|
||||
```
|
||||
|
||||
Haskell and OCaml use a concept called
|
||||
[*currying*](https://en.wikipedia.org/wiki/Currying) or partial function
|
||||
@@ -113,13 +127,15 @@ numbers, pass the partially applied function `printf "%x"` to `map`,
|
||||
[like
|
||||
so](https://github.com/vigetlabs/otp/blob/master/languages/Haskell/encrypt.hs#L12):
|
||||
|
||||
hexStringOfInts nums = concat $ map (printf "%x") nums
|
||||
```haskell
|
||||
hexStringOfInts nums = concat $ map (printf "%x") nums
|
||||
```
|
||||
|
||||
For more info on currying/partial function application, check out
|
||||
[*Learn You a Haskell for Great
|
||||
Good*](http://learnyouahaskell.com/higher-order-functions).
|
||||
|
||||
## A Friendly Compiler {#afriendlycompiler}
|
||||
## A Friendly Compiler
|
||||
|
||||
I learned to program with C++ and Java, where `gcc` and `javac` weren't
|
||||
my friends -- they were jerks, making me jump through a bunch of hoops
|
||||
@@ -129,30 +145,36 @@ worked almost exclusively with interpreted languages in the intervening
|
||||
languages with compilers that catch real issues. Here's my original
|
||||
`decrypt` function in Haskell:
|
||||
|
||||
decrypt ciphertext key = case ciphertext of
|
||||
[] -> []
|
||||
c1:c2:cs -> xor (intOfHexChars [c1, c2]) (getMask key) : decrypt cs (drop 2 key)
|
||||
```haskell
|
||||
decrypt ciphertext key = case ciphertext of
|
||||
[] -> []
|
||||
c1:c2:cs -> xor (intOfHexChars [c1, c2]) (getMask key) : decrypt cs (drop 2 key)
|
||||
```
|
||||
|
||||
Using pattern matching, I pull off the first two characters of the
|
||||
ciphertext and decrypt them against they key, and then recurse on the
|
||||
rest of the ciphertext. If the list is empty, we're done. When I
|
||||
compiled the code, I received the following:
|
||||
|
||||
decrypt.hs:16:26: Warning:
|
||||
Pattern match(es) are non-exhaustive
|
||||
In a case alternative: Patterns not matched: [_]
|
||||
```
|
||||
decrypt.hs:16:26: Warning:
|
||||
Pattern match(es) are non-exhaustive
|
||||
In a case alternative: Patterns not matched: [_]
|
||||
```
|
||||
|
||||
The Haskell compiler is telling me that I haven't accounted for a list
|
||||
consisting of a single character. And sure enough, this is invalid input
|
||||
that a user could nevertheless use to call the program. Adding the
|
||||
following handles the failure and fixes the warning:
|
||||
|
||||
decrypt ciphertext key = case ciphertext of
|
||||
[] -> []
|
||||
[_] -> error "Invalid ciphertext"
|
||||
c1:c2:cs -> xor (intOfHexChars [c1, c2]) (getMask key) : decrypt cs (drop 2 key)
|
||||
```haskell
|
||||
decrypt ciphertext key = case ciphertext of
|
||||
[] -> []
|
||||
[_] -> error "Invalid ciphertext"
|
||||
c1:c2:cs -> xor (intOfHexChars [c1, c2]) (getMask key) : decrypt cs (drop 2 key)
|
||||
```
|
||||
|
||||
## Elixir's \|\> operator {#elixirsoperator}
|
||||
## Elixir's |> operator
|
||||
|
||||
According to [*Programming
|
||||
Elixir*](https://pragprog.com/book/elixir/programming-elixir), the pipe
|
||||
@@ -167,18 +189,22 @@ argument passed into the program, convert it to a list of characters,
|
||||
and then turn it to a cyclical stream. My initial approach looked
|
||||
something like this:
|
||||
|
||||
key = Stream.cycle(to_char_list(List.first(System.argv)))
|
||||
```elixir
|
||||
key = Stream.cycle(to_char_list(List.first(System.argv)))
|
||||
```
|
||||
|
||||
Using the pipe operator, we can flip that around into something much
|
||||
more readable:
|
||||
|
||||
key = System.argv |> List.first |> to_char_list |> Stream.cycle
|
||||
```elixir
|
||||
key = System.argv |> List.first |> to_char_list |> Stream.cycle
|
||||
```
|
||||
|
||||
I like it. Reminds me of Unix pipes or any Western written language.
|
||||
[Here's how I use the pipe operator in my encrypt
|
||||
solution](https://github.com/vigetlabs/otp/blob/master/languages/Elixir/encrypt#L25-L31).
|
||||
|
||||
\* \* \*
|
||||
***
|
||||
|
||||
At the end of this process, I think Haskell offers the most elegant code
|
||||
and [Elixir](https://www.viget.com/services/elixir) the most potential
|
||||
|
||||
Reference in New Issue
Block a user