Monday, April 11, 2016

False Cognates in Haskell

False cognates in language are a phenomenon where two words that sound very similar in one language actually mean something very different. For example, in Spanish, asistir (which sounds like the English "assist") actually means "to attend." And sopa, which sounds like "soap," actually means "soup."

But it's not just natural language: this phenomenon exists in programming languages, too. In fact, Haskell seems to do it on purpose.

The Trouble with return

Here's an example. Even if you don't know what a monoid in the category of endofunctors is, you might be able to understand this snippet of code:
main = do
         putStrLn "Hello"
         putStrLn "World!"
         return ()
All you need to know, really, is that putStrLn means "print a line", and you basically know that this bit of code will print out the lines "Hello" and "World" and then return. This seems very sensible, and in fact, it seems like it could be useful that the idioms used in Haskell are so similar to the idioms used in procedural programming languages; after all, it means you can understand this snippet, even if you've never written a line of Haskell.

But here's the tricky part. Now let's try modifying the behavior of this code snippet.
main = do
         putStrLn "Hello"
         return ()
         putStrLn "World!"
What would you guess this modified snippet does, if you're coming from a non-Haskell programming language? You'd probably think it would only print out "Hello", and then be finished. Not so. In fact, this code snippet does the exact same thing as the one above.

The exact reasons for this are best explained elsewhere (personally, my favorite monad tutorial is the Typeclassopedia), but the upshot is, it doesn't work the same way at all. Haskell happens to use the keyword return for something that is actually quite different from what it means in a procedural programming language.

And it just so happens that there are a few contexts where making that keyword be return makes a Haskell function look similar to a procedural one. I can only assume that whoever came up with this was so pleased with the pun that they decided to use that keyword to describe it.1

Other Examples

The problem doesn't end with monads. With apologies to the author of this lens tutorial, here's a snippet of a text game written in Haskell: the function for striking your enemy:
strike = do
    lift $ putStrLn "*shink*" -= 10
"Wow!" you might think, "you can modify objects with a dot operator, just like in a procedural language!"

Wrong again. That dot, though it does a similar thing to the boss in the game, represents function composition. Though I think lens does a better job in most cases at maintaining an actual abstraction, rather than just punning with its operators, it can still often be opaque: modifying lens programs (without a deep understanding of how the library works) is often a frustrating trial-and-error process. Basically, if your model of the program is "it works by modifying the object, just like in an object-oriented language," you're pretty hosed; you won't be able to make any useful changes to it.

Another example of a visual pun in a Haskell library, punning on math rather than other programming languages, is in HMatrix. Suppose you wanted to represent a 2 3 matrix; in math, we would write

With the HMatrix library, you can define a matrix this way:
 >>> let matrix =
    (2><3) [1, 2, 3,
            4, 5, 6]
This is cute. It's visually similar to how you would describe a matrix as "2 3"; under the hood, (><) is a function that takes the rows and columns as arguments. This is a relatively harmless example of the tendency toward punning, since it's just one function and there aren't many complex modifications you'd want to make to it.

Concluding thoughts

Making something in one programming language resemble another doesn't do much good if the underlying abstraction doesn't match up with what you're imitating. In fact, it can be confusing for beginners, and discouraging when they realize their understanding was much worse than they initially thought.

1 I don't actually know the history of why return was the chosen keyword for monadic pure; I'd be happy to be educated on this subject.

Credit to Joe Begriffs for the term "false cognates."

No comments :

Post a Comment