changeset 94:eb868f089bd1

Add notes and examples on data types and monads Still to do: Day 3 exercises
author IBBoard <dev@ibboard.co.uk>
date Sun, 23 Jun 2019 20:13:55 +0100
parents 39084e2b8744
children 83b4f23b9df8
files 7-Haskell/README.txt 7-Haskell/monad.hs
diffstat 2 files changed, 98 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/7-Haskell/README.txt	Tue Jun 18 21:05:00 2019 +0100
+++ b/7-Haskell/README.txt	Sun Jun 23 20:13:55 2019 +0100
@@ -57,3 +57,68 @@
     let double = prod 2
     let triple = prod 3
 double and triple are partially applied functions, and the only (sane) way to be able to do that is if "prod x y" is a two-part function! The book says "When Haskell computes prod 2 4, it is really computing "(prod 2) 4". [insert anonymous functions]". This is called currying.
+
+Haskell has "data types" that use the 'data' keyword to specify an allowed set of values, and "types" that reference data types, e.g.:
+    data Suit = Spades | Hearts
+    data Rank = Ten | Jack | Queen | King | Ace
+    type Card = (Rank, Suit)
+    type Hand = [Card]
+Suit can be Spades or Hearts, Rank can be Ten through Ace (no ordering implied here because we don't use "deriving Ord"), a Card is a tuple of a rank and a suit, and a Hand is a list of cards.
+This will fail to print values in the console unless we add "deriving (Show)" to the end of the "data …" lines.
+
+The book isn't clear what makes a "data" vs a "type", but it appears at first to be about whether it refers to values or other "classes" ('data' types).
+Except that on the next page the "Triplet" definition doesn't meet that criteria.
+The Haskell wiki (https://wiki.haskell.org/Type) says types are defined using "data" and then "type" introduces synonyms.
+The examples are:
+    data Maybe a = Just a | Nothing
+(Define "Maybe" as a polymorphic of type variable "a" as "Just a thing of that type 'a'" or "Nothing")
+    data Tree a = Branch (Tree a) (Tree a) | Leaf a
+(Define "Tree" as a polymorphic of type variable "a" as a Branch of two trees of type a or a Leaf of type a - you'd assume Branch and Leaf are defined elsewhere, but apparently this is enough to define them implicitly!)
+
+Note: The wiki defines Card as a "data", not as a "type". It also does it quite differently, using "Card = Card { value :: Rank, suit :: Suit } …", which seems recursive at first but is probably just like "class MyClass { public MyClass(var a) { … } }".
+Also from the wiki, if you "deriving" something like Ord or Enum then you need to use "instance [derived thing] [my type] where … [implementation]"
+
+Haskell also includes "classes", but they're not like Object-Oriented classes. They define which operations work on which inputs (e.g. Bool + Bool doesn't work, but Integer + Integer does). It's a bit more like an interface in that it defines functionality
+and guarantees functions exist BUT with optional default implementation. For example, in the following definition:
+
+class Eq a where
+    (==), (/=) :: a -> a -> Bool
+    x /= y = not (x == y)
+    x == y = not (x /= y)
+
+This means nothing on its own, but any data type "deriving Eq" will work with "==" and "/=" and you only need to implement one for your type to get the other for free.
+
+Monads. Monads are about function composition. They let you do the equivalent of "v = func1(v); v = func1(v); v = func2(v); return v" from imperative languages.
+You can do this pattern with a multi-line "let … in [return_value]" *but* you can't reuse variable names.
+
+Monads have a type constructor that takes a container, a function called "return" to wrap a function in the container, and a bind function called ">>=" that unwraps and chains the function. See monad.hs.
+
+A built-in use of monads is the "do" notation, which gives an imperative style on the surface but is implemented with monads:
+
+    tryIo = do putStr "Enter your name: " ;
+               line <- getLine ;
+               let { backwards = reverse line } ;
+               return ("Hello. Your name backwards is " ++ backwards)
+
+That's a function call "tryIo" that uses "do" to look imperitive. This is just syntactic sugar so that you don't have to type ">>=" so often. The semi-colons are optional, and you can wrap the code in "{}"
+
+A list is already defined as a monad using "instance". The definitions are simple enough:
+
+instance Monad [] where
+    m >>= f = concatMap f m
+    return x = [x]
+
+So we define a monad on the list (top line) then define the binding, which applies the function to every value in the list, and then the return value is a list of the value.
+This allows lists to be used in "do …" blocks to write a "for each item in an array return a value" function, such as:
+    let cartesian (xs, ys) = do x <- xs; y <- ys; return (x, y)
+Here, we take in two lists (xs and ys) and for each pair x and y we return the tuple BUT the monad definition and the "do" operation means that we get a list returned:
+    Main> cartesian ([1..2], [3..4])
+    [(1,3), (1,4), (2,3), (2,4)]
+The book says this is "an alternate computational strategy" to list comprehension, but it reads almost entirely like list comprehension. The only difference in this example is that the "return" is at the end. c.f.:
+    let cartesian_list (xs, ys) = [(x,y) | x <- xs, y <- ys]
+The password cracking example is a little more complex but could still probably be done with list comprehension.
+
+"Maybe" is also a monad on the Maybe data type. The type is defined as "Nothing" or "Just a". The Monad returns is an alias for "Just" and so the result is "Just x" and there are two ways of binding the function.
+    Nothing >>= f = Nothing
+    (Just x) >>= f = f x
+i.e. there are no "null pointer exception"-type problems for chaining function calls as applying a function to Nothing safely returns Nothing.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/7-Haskell/monad.hs	Sun Jun 23 20:13:55 2019 +0100
@@ -0,0 +1,33 @@
+module MyMonad where
+    -- Data type "Position" takes a thing of type "t" and is defined as itself.
+    -- We also derive "Show" so it'll print
+    -- This is the container
+    data Position t = Position t deriving (Show)
+
+    -- Define some functions
+    stagger (Position d) = Position (d + 2)
+    crawl (Position d) = Position (d + 1)
+
+    -- Custom "return" function - just return the value
+    -- Using "return" would clash with the built-in Monad function
+    rtn x = x
+
+    -- Custom "bind" function. Using ">>=" would clash with the built-in Monad function.
+    -- Binding a function to "x" results in the function being invoked with x as an argument
+    x >>== f = f x
+
+-- Usage:
+-- treasureMap pos = pos >>== stagger >>== stagger >>== crawl >>== rtn
+-- treasureMap (Position 0)
+-- returns: Position 5
+-- "pos" is a Position data type, so fits the "stagger" and "crawl" definitions
+-- "pos >>== stagger" is equivalent to "stagger pos"
+-- "pos >>== stagger >>== stagger" is equivalent to "stagger (stagger pos)"
+-- and so "pos >>== stagger >>== rtn" is equivalent to "rtn (stagger pos)", which returns the result of "stagger pos"
+
+
+-- cartesian takes a tuple of values. For each value in xs and xy we return (x, y)
+let cartesian (xs, ys) = do x<- xs; y <- ys; return (x, y)
+-- Call it on a pair of coordinates
+cartesian ([1..2], [3..4])
+-- Output: [(1,3), (1,4), (2,3), (2,4)]