Basics of Haskell Learning II (Type System)
29 Oct 2019 | Haskell | developmentInteresting static type system in Haskell: has type reference and can infer the type on its own. Not only for explicit types (capitalized type like Char), but also complex types like list, tuple..
Prelude> :t 'a'
'a' :: Char
Prelude> :t "Hello"
"Hello" :: [Char]
:t (1, "abc")
(1, "abc") :: Num a => (a, [Char])
Functions also have types. Explicit type declaration looks like:
//put the following two lines in baby.hs
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
//load baby module from ghci
Prelude> :l baby
*Main> :t removeNonUppercase
removeNonUppercase :: [Char] -> [Char]
*Main> removeNonUppercase "Hello, world"
"H"
As a Java developer, I feel a bit wierd with Haskell’s function type declaration, eg. addThree :: Int -> Int -> Int -> Int means the function takes 3 Int parameters and return a Int as a result. The returning type is defined at the last ‘->’, not explicitly declared like Int, Int, Int -> Int or something.
Functions with type variables are polymorphic functions, like the head function receives a list of anything, then returns the first instance of the input list.
Typeclasses
I’ll start with some examples which were also mentioned in the Haskell guide, but I added some more experiments on perspective of a Java developer.
Prelude> 'c' == 'b'
False
Prelude> 'c' == 2
<interactive>:8:8: error:
• No instance for (Num Char) arising from the literal ‘2’
• In the second argument of ‘(==)’, namely ‘2’
In the expression: 'c' == 2
In an equation for ‘it’: it = 'c' == 2
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool
Prelude> :t elem
elem :: (Foldable t, Eq a) => a -> t a -> Bool
Prelude> :t (>)
(>) :: Ord a => a -> a -> Bool
Prelude> :t read
read :: Read a => String -> a
Prelude> read "\"hello\""
*** Exception: Prelude.read: no parse
Prelude> read "\"hello\""::[Char]
"hello"
The (==) function is defined to receive two parameters of the same type and return a Bool, thus Char ‘c’ and Integer 2 cannot be compared.
But what’s the => in the definition? Class constraint. For the example mentioned, the type of those two values must be a member of the Eq class.
Eq is a type class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.
Ord is also a type class showing types that have orders.
Interesting facts about Read, noticed the last two examples of Read? The read function consumes a String and return it as a typed instance, the first example is because read does not know what type the return value is, so the second explicitly applies a type then it works.
However, I had anther try with read, hoping to concat two Strings (also the [Char]) together.
Prelude> read "\"hello\""::[Char] ++ ", world"
<interactive>:36:26: error:
Not in scope: type constructor or class ‘++’
<interactive>:36:26: error:
Illegal operator ‘++’ in type ‘[Char] ++ ", world"’
Use TypeOperators to allow operators in types
<interactive>:36:29: error:
Illegal type: ‘", world"’ Perhaps you intended to use DataKinds
What’s wrong? There are several errors..I reviewed the function execution priority, and got the answer. I updated my code to:
Prelude> read "\"hello\"" ++ ", world"
"hello, world"
Althogh read is of top priority, but ::[Char] does not share the same priority with read, then the errored one is executed as read “"hello"” following the type ([Char] ++ “, world”). I guess:), and I’ll update the conclusion if I’m wrong after I dig into HasKell deeper.
After all the exploration above, the typeclass in Haskell, in my perspective, is more likely understood as Java’s interface, NOT actual Java class. It is declaring what the type is, but no implementations, looks like a protocal, or interface.