Recently with my colleagues from Wix, I started reading about Typed Tagless Final Interpreters. As with any good material on programming paradigms, it is filled with complicated code examples and complex word constructs. So I decided to relieve some complexity by at least explaining some Haskell constructs used in the code.
Algebraic Data Types
Define syntax used to describe algebra.
data Bool = True | False
In the example above
Bool represents a data type and
False represents values it can be evaluated too. So if you use a
Bool type in your code it’s gonna be either
data Maybe a = Just a | Nothing
In some cases we want our data types to also support type arguments. To do so we provide a placeholder for the type. In the example above for
Maybe we provide a type argument
a that when used describes the type of
Just . So
Maybe Int will be either
Just Int or
data Either a b = Left a | Right b
This is also not just limited to single arguments. You can as easily provide multiple of them and use them on represented value constructors. So
Either Int String can either be
Left Int or
Right String .
One of the hardest things to read ,when I started using Haskell, was the function signatures. Initially, they seem opaque and cryptic and even might discourage the reader from trying to understand them.
add :: Integer -> Integer -> Integer
add a b = a + b
So the function above describes a simple addition. It might be confusing that the type is described as a chain of arrows and types. This is because Haskell is curried(each function has only one argument, if there are more passing part of the arguments returns a function that would let you pass the rest of them). It’s easier to read if you know that
-> is right associative. That just means that type signature above is the same as
add :: Integer -> (Integer -> Integer)`.
addFn :: (Integer -> Integer) -> Integer
addFn fn = fn 2
By adding brackets around type arguments, we are saying that we will pass something that is a function that takes
Integer and returns
Integer . This way we can describe passing function around.
foo :: (Show a) => a -> a -> String
foo x y = show x ++ show y
It’s quite common to see type constraint being added to functions to make them more generic. The complicated thing here for me was that types and function parameters live in different space. So
(Show a) constrain only applies at the type level. You might wonder where did the
show method come from? The answer is pretty simple, it’s the type constraint. Show convert any passed type to
String , so in the function type above we state, that any parameter passed to this function as
y need to have a
show method and because we used the same type for both arguments, there types must match. If you would like to have different type you can express function above like this:
foo :: (Show a, Show b) a -> b -> String
foo x y = show x ++ show y
You might wonder, how do I know what method Show constraint allows? I will cover this in my next post!
It might feel that what you just learned is not much, but it’s a cornerstone to reading any good computer science paper using Haskell. If you enjoyed this article please follow me on Medium and get the latest update from my Twitter profile.