==========================[ Readings ]========================== Read Chapters 3--6 of "Read World Haskell". You may skim Ch. 5 at a first reading, but try examples from all others at your GHCI command line (or in Emacs buffer) to follow along! =====================[ Getting used to Haskell syntax ]===================== $ ghci GHCi, version 7.8.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. // Recall that : and [] are the Haskell list constructors. // They are as fundamental to Haskell lists as CONS and NIL // are to LISP's---they are the only an Prelude> 1 : 2 : [] [1,2] // By itself, [] has a type of "list of any single kind of thing t" // When used with values of specific types, t will // be constrained by whatever types these values have. Prelude> :t [] [] :: [t] Prelude> :set +m Prelude> :t 1:[] 1:[] :: Num a => [a] // Looking up if functions are defined, and where. Prelude> :i sum sum :: Num a => [a] -> a -- Defined in ‘Data.List’ Prelude> :i + class Num a where (+) :: a -> a -> a ... -- Defined in ‘GHC.Num’ infixl 6 + // map has an interesting type: note that the function // (a -> b) makes as into bs, and so map takes a // list of as into a list of bs. But a and b need // not be the same; unification doesn't demand this. Prelude> :i map map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’ // for some reason, multiline input didn't work; // note that the second prompt we got wasn't // Preline| as should be with multiline. // Recall that GHCI needs a let for functions defined // at the top level (but not in a file that's :load-ed). Prelude> let mysum [] = 0 Prelude> mysum (x:xs) = x + mysum xs :9:18: parse error on input ‘=’ // Fine, let's use the one-liner syntax with ; Prelude> let mysum [] = 0 ; mysum (x:xs) = x + mysum xs Prelude| // Testing Prelude> mysum [] 0 Prelude> mysum [1,2,3] 6 Prelude> :t mysum mysum :: Num a => [a] -> a // Let us now create our own List type. // Haskell has a standard one in Data.List, // but ours will be isomorphic to it. Prelude> :i List Top level: Not in scope: data constructor ‘List’ Prelude> :i Nil Top level: Not in scope: data constructor ‘Nil’ // Good, these are not in scope; so define them as we like: Prelude> data List a = Nil | Cons a (List a) // This works: our expression typechecks Prelude> :t Cons 1 (Cons 2 Nil) Cons 1 (Cons 2 Nil) :: Num a => List a // ..and type inference works for it just like for built-ins: Prelude> :t Cons 'a' (Cons 'b' Nil) Cons 'a' (Cons 'b' Nil) :: List Char // NOTE that I didn't just create a type---I created a whole family // of types, List a, for any type a (a.k.a. "parametrized by a"). // You can do this in C++ and recent Java with templates, but not // so gracefully. // Even though this type-checks, Haskell doesn't know how to print it. // GHCI's REPL uses the function 'show', and it argument must belong // to the typeclass Show (just like + means Num). // Prelude> Cons 'a' (Cons 'b' Nil) :28:1: No instance for (Show (List Char)) arising from a use of ‘print’ In a stmt of an interactive GHCi command: print it Prelude> [4]+ Stopped ghci // The definition of show for my List is a bit more complex syntactically // than convenient to type at the GHCI prompt, so I switched to Emacs // and did M-x shell to start my Bash shell in a buffer, then started // GHCI there. hs user$ emacs mylist.hs // Pasting from Emacs shell Prelude> :l "mylist.hs" [1 of 1] Compiling Main ( mylist.hs, interpreted ) // We'll ignore this warning for now. It comes from my variant // of the tail function. mylist.hs:16:1: Warning: Pattern match(es) are overlapped In an equation for ‘tl’: tl (Cons _ Nil) = ... Ok, modules loaded: Main. // Now that I defined show for my new type (List a), its objects // now print fine with my custom printing function. *Main> Cons 1 (Cons 2 Nil) <1 <2 <>>> // rereading a file is easy from GHCI command line *Main> :r [1 of 1] Compiling Main ( mylist.hs, interpreted ) // I had both the "deriving" in the class declaration, and a // defined instance of Show (List a). They conflicted, hence // the error: mylist.hs:2:22: Overlapping instances for Show (List a) arising from the second field of ‘Cons’ (type ‘List a’) Matching instances: instance Show (List a) -- Defined at mylist.hs:2:22 instance Show a => Show (List a) -- Defined at mylist.hs:7:10 When deriving the instance for (Show (List a)) Failed, modules loaded: none. // Commented 'deriving out', reloading Prelude> :r [1 of 1] Compiling Main ( mylist.hs, interpreted ) mylist.hs:22:1: Warning: Pattern match(es) are overlapped In an equation for ‘tl’: tl (Cons _ Nil) = ... Ok, modules loaded: Main. // GHCI REPL doesn't complain now & uses my show: *Main> Cons 1 (Cons 2 Nil) <1 <2 <>>> // But I still need to write parens when building lists, // because without parens Haskell's parser sees // a call to Cons with *four* parameters 1, Cons, 2, and Nil, // not what we intended! *Main> Cons 1 Cons 2 Nil :7:1: Couldn't match expected type ‘a2 -> List a3 -> t’ with actual type ‘List a0’ Relevant bindings include it :: t (bound at :7:1) The function ‘Cons’ is applied to four arguments, but its type ‘a0 -> List a0 -> List a0’ has only two In the expression: Cons 1 Cons 2 Nil In an equation for ‘it’: it = Cons 1 Cons 2 Nil :7:8: Couldn't match expected type ‘List a0’ with actual type ‘a1 -> List a1 -> List a1’ Probable cause: ‘Cons’ is applied to too few arguments In the second argument of ‘Cons’, namely ‘Cons’ In the expression: Cons 1 Cons 2 Nil // So we want an infix function to build lists without parens, just like : // Let's check what : is: *Main> :i : data [] a = ... | a : [a] -- Defined in ‘GHC.Types’ infixr 5 : data [] a = ... | a : [a] -- Defined in ‘GHC.Types’ infixr 5 : // Note that + associates to the _left_ (infixl) whereas : associates // to the _right_: *Main> :i + class Num a where (+) :: a -> a -> a ... -- Defined in ‘GHC.Num’ infixl 6 + // So I add a "infixr 5 :" declaration to the file, and // now the GHCI parser sees it right: *Main> :r [1 of 1] Compiling Main ( mylist.hs, interpreted ) mylist.hs:3:10: parse error on input ‘cons’ Failed, modules loaded: none. Prelude> :r [1 of 1] Compiling Main ( mylist.hs, interpreted ) mylist.hs:26:1: Warning: Pattern match(es) are overlapped In an equation for ‘tl’: tl (Cons _ Nil) = ... Ok, modules loaded: Main. // Now it works: cons is a function, `cons` is infix right-associative *Main> cons 1 Nil <1 <>> *Main> 1 `cons` Nil <1 <>> *Main> 1 1 *Main> 1 `cons` 2 `cons` Nil <1 <2 <>>> // Adding my length function for List: Prelude> :r [1 of 1] Compiling Main ( mylist.hs, interpreted ) mylist.hs:30:1: Warning: Pattern match(es) are overlapped In an equation for ‘tl’: tl (Cons _ Nil) = ... Ok, modules loaded: Main. *Main> len (1 `cons` 2 `cons` Nil) 2 // Alternative syntax with $ *Main> len $ 1 `cons` 2 `cons` Nil 2 *Main> //// Lazy evaluation: GHCi, version 7.8.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> :i cond Top level: Not in scope: ‘cond’ // Writing my own functional form of IF . In LISP & Scheme, // which use eager evaluation of function arguments, // such a function cannot exist---it must be a special form. // In Haskell, due to lazy evaluation, this can be a normal function, // because in Haskell functions only evaluate their arguments // if needed, and _don't_ evaluate them otherwise. // Argh, typo: Prelude> let cond tst thn els = if tst then tt else els :4:36: Not in scope: ‘tt’ Perhaps you meant ‘tst’ (line 4) // Now it works. Note that if .. then .. else .. in Haskell // forces the expressions after then and after else to have // the same type! "if True then 1 else 'a'" won't type-check! Prelude> let cond tst thn els = if tst then thn else els Prelude> cond (1>0) 1 (-1) 1 Prelude> cond (1>0) 'a' 'b' 'a' // error, when evaluated, causes an immediate exception. // So it's evidently _not_ evaluated! Prelude> cond (1>0) 'a' (error "boom") 'a' // ..and now it is: Prelude> cond (1>2) 'a' (error "boom") *** Exception: boom // Laziness allows Haskell to define infinite list objects like [1..] // (all integers starting from 1) Prelude> [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> take 2 [1..10] [1,2] Prelude> take 2 [1..] [1,2] // Finally, a short demo of filter. Note its type: Prelude> :i filter filter :: (a -> Bool) -> [a] -> [a] -- Defined in ‘GHC.List’ Prelude> filter (\x -> x > 2) [1,2,3,4] [3,4] // With partial application, we don't even need the explicit lambda: Prelude> :t (>) 2 (>) 2 :: (Ord a, Num a) => a -> Bool Prelude> filter ((>) 2) [1,2,3,4] [1] Prelude> filter ((<=) 2) [1,2,3,4] [2,3,4] Prelude> filter ((<) 2) [1,2,3,4] [3,4]