r2 - 07 Feb 2020 - 16:24:46 - MelissaONeillYou are here: TWiki >  CS131Spring2020 Web  > Week3Class2

Warmup & Review

Warmup: Desugaring

Rewrite this code in desugared form (i.e., the lambda calculus) with all the implicit parentheses we used last week and no use of infix operators.

  • twice x = x + x
  • more y = twice y+y
  • map f list
  • 1 : [2,3]
  • f a b+g c d

Answers:

twice x = x + x

  • \x.(((+) x) x)
  • We also discussed its type. The first thing plus takes in is an Int, and this produces an (Int -> Int) function.
  • (+):: Int -> (Int->Int)
  • twice:: Int -> Int

more y = twice y+y

  • \y.(((+)(twice y)) y)
  • Remember that a function operation is more tightly binding than an operator.

map f list

  • ((map f) list)
  • Remember that a function operation is more tightly binding than an operator.
  • A true map function in lambda calculus would require recursion, but we did not go that far in class.
  • This is case of currying functions, where an intermediate function would be returned.

1 : [2,3]

  • ((Cons 1) ( (Cons 2) ((Cons 3) Nil) ) )
  • Remember that the second argument to Cons must be a list
  • This can also be represented as
  • 1 : ( 2 : ( 3 : [] )) or *( ((:)1) ((:)2 ((:)3 [])) )

f a b+g c d

  • (((+)((f a)b))((g c)d))
  • Remember that Haskell adds implicit parenthesis for curried functions to the left and that spacing does not matter, only parenthesis do.

Warmup: Valid or Invalid

For each piece of code (expression or declaration), is it okay, or is it nonsensical in some way (if it doesn't make sense, what is wrong?)

  • 1 + 1
  • 1 +
  • Int + 1
  • test1 :: Int -> Int -> Bool
  • test2 :: Int -> 3 -> Bool
  • test3 :: True -> False -> True
  • f 0 = 0
  • f Int = 0
  • g [] = True
  • g h:t = False

Answers:

1 + 1

  • Okay

1 +

  • Nonsensical.
  • You need parentheses to make this a function.
  • (1 +) or (+ 1) would both be fine, and are syntactic sugar for (\x. 1 + x) and (\x. x + 1) respectively.
  • With parenthesis, this would be valid for any binary operator.

Int + 1

  • Nonsensical.
  • You can't add a type to a number (or any value).

test1 :: Int -> Int -> Bool

  • Okay
  • This is implicitly understood as Int -> (Int -> Bool). That is, test1 takes in an Int and returns a function that takes in an Int and returns a Bool.
  • Implicit () for types go to the right (but in curried functions they go to the left).

test2 :: Int -> 3 -> Bool

  • Nonsensical.
  • 3 is not a type.

test3 :: True -> False -> True

  • Nonsensical.
  • True and False are Booleans, not types.

f 0 = 0

  • Okay.
  • We use things like this for pattern matching.

f Int = 0

  • Nonsensical.
  • We can't pattern match on types.
  • The only way to make this work would be to add a value called Int, which is possible to do in Haskell.

g [] = True

  • Okay.
  • This is also pattern matching.

g h:t = False

  • Nonsensical.
  • The parser tries to do (g h):t.
  • Remember that functions bind tightly.
  • Use parentheses with complicated arguments.
  • g (h:t) = False

Additional Examples:

  • ('2', True)
  • ['2', True]

Answers:

('2', True)

  • Okay.
  • Tuples can have elements with different types.

['2', True]

  • Nonsensical.
  • Lists must have elements that are all the same type.

Wiki Extra: Guards, if and case statements

In this bonus material, we'll play with the factorial function!

Some neat ways to write the factorial function include

fact n = product [1..n]

fact n = foldr (*) 1 [1..n]

fact n = foldr1 (*) [1..n]

You may want to look at the type of foldr and foldr1 to see how these work.

But normally we're write factorial recursively, like this:

fact 0 = 1
fact n = n * fact (n-1)

This uses pattern matching. It's basically syntactic sugar for a case statement like this

fact n = case n of 0 -> 1
                   _ -> n * fact (n-1)

Generally, we like pattern matching over if statements, but we could use an if statement like this:

fact n = if n < 1 then 1
                  else n * fact (n-1)

But a nicer notation than an if is often to use guard syntax:

fact n | n < 1       = 1
       | otherwise   = n * fact (n-1)

If you look on-line, people will often show guards with multiple clauses ending with otherwise (which is just the same as True), but you can just have one clause. If none of the guards are true, matching proceeds with the next pattern, so we could write the base case last like this:

fact n | n >= 1    = n * fact (n-1)
fact _             = 1

Here's a better example of a guard without an otherwise:

commonPrefix (x:xs) (y:ys) | x == y   = x :: commonPrefix xs ys
commonPrefix _      _                 = []
if we wrote this code with an if, it'd look like this
commonPrefix (x:xs) (y:ys) = if x == y then x :: commonPrefix xs ys
                                       else []
commonPrefix _      _      = []
Notice that in this version, we have to list the [] result twice, which is less cool.

Types & Typeclasses

What's the Type?

  1. 'c'
  2. True
  3. [True,False]
  4. ('c', True, GT)
  5. "Hello"
  6. ['h','e','l','l','o']
  7. \x -> x ++ " World"
  8. \x -> x
  9. \x -> \y -> (x,y)
  10. \x -> \y -> [x,y]
  11. \x -> \y -> x == y
  12. \x -> "--> " ++ show x
  13. \x -> x+1
  14. 42

Here's what you get when you enter each expression into ghci with :t

  1. 'c' :: Char
  2. True :: Bool
  3. [True, False] :: [Bool]
  4. ('c', True, GT) :: (Char, Bool, Ordering)
    • In other words: "Triple with Char, Bool, Ordering"
    • We haven't seen this in practice yet, but Ordering is a type with values LT, EQ, and GT.
  5. "Hello" :: [Char]
    • Note that String is really just the same thing as [Char]. Haskell will default to the latter, unless you explicitly type something using String, as in let mystring :: String in ghci.
  6. ['h','e','l','l','o'] :: [Char]
    • This is equivalent to the previous, but without syntactic sugar.
  7. \x -> x ++ " World" :: [Char] -> [Char]
    • Neat! Haskell automatically infers the types for the function's input and output.
  8. \x -> x :: a -> a
    • This has an implicit "for all types a"
  9. \x -> \y -> (x,y) :: a -> b -> (a, b)
  10. \x -> \y -> [x,y] :: a -> a -> [a]
    • ...and it infers the need for type variables.
    • Remember that lists are homogeneous.
  11. \x -> \y -> x == y :: Eq a => a -> a -> Bool
    • ...and for typeclasses.
    • Recall that Eq is a typeclass wherein all types define the == operator. Eq contains many types, but notably does not include functions (since function equivalence is undecidable).
    • Recall that == operates only on two arguments of the same type, so you can't for instance compare a Float with an Int. However, in the case of something like 3 == 3.2, Haskell simply infers that both arguments are Floats.
  12. \x -> "--> " ++ show x :: Show a => a -> String
    • Recall that Show is a typeclass wherein all types define the show operator, which turns a value into a string.
  13. \x -> x+1 :: Num a => a -> a
  14. 42 :: Num a => a

Class Notes:

Values, Types, and Typeclasses

In Haskell, Values are instances of Types. This relationship is similar to set membership where the values are members of types. Example values include:

  • 'c'
  • True
  • GT (and LT and EQ)
  • [True, False]
  • "Hello"
  • 42

The Types of these values, respectively, are:

  • Char
  • Bool
  • Ordering
  • [Bool]
  • [Char]
  • At this point, we cannot know, as there are many types that encode numerical values. You can give this value an explicit type by writing 42 :: Integer instead.

Above Types, there are Typeclasses. These are detailed in the next section, and help by grouping types by which operations can be performed on them. For instance, by writing the function \x -> \y -> x < y, Haskell knows that x and y must be values of the same type, which is part of the Ord Typeclass (which contains all types which have a defined method of comparison).

Haskell's families of types — Typeclasses

Recall that values have types. Types belong to type classes.

  • Eq defines the == and /= operators
  • Ord defines comparison operators, such as <
    • Note that any type in Ord must also be in Eq
  • Bounded defines minBound and maxBound, so for instance, minBound :: Int gives -2147483648
  • Enum defines the succ and pred operators, and also allows a type to be used in list ranges
  • Read defines the read operator which turns a String into a value
    • Note that Haskell needs to be able to infer what the resulting type should be, so something like read "4" + 2 or (read "4") :: Int will work, but simply read "4" will not.
  • Show defines the show operator which turns a value into a String.
  • Num defines +, -, /, etc...

Haskell's data Declarations

Haskell is not an object oriented language, so there are no classes, but rather types and type classes.

Some types are predefined like Bool and Ordering, but there's nothing really special about these types. Here are their type signatures:

data Bool = False | True

data Ordering  =  LT | EQ | GT 

Here Bool and Ordering are types and False, True, LT, EQ, and GT are values.

Note: Ordering is a type while Ord is a typeclass. They are not the same thing!

Defining Our Own Types

We are able to define our own types by using data declarations.

data CoinFlip = Heads | Tails

data Suit = Clubs | Diamonds | Hearts | Spades

data ThermostatSetting = Off 
                       | Cooling 
                       | Heating

These newly defined types will not belong to any typeclasses, such as Eq or Show, even though you will be able to use them as literal constants in expressions and pattern matching. You might also think you can “see them” when you use :t on an expression (because it just prints what you typed and so it looks like it's printing out values of the type even though it isn't). To get Haskell to include the corresponding "obvious" definitions of == and show, you use deriving, as shown below.

If you wanted make sure Bool could be used where things of the Show type class are used, we could define show for the type Bool.

instance Show Bool where
  show :: Bool -> String
  show True = "True"
  show False = "False"

Similarly, here is how you would define the == operator on Bools.

instance Eq Bool where
  (==) :: Bool -> Bool -> Bool
  (==) True  True  = True
  (==) False False = True
  (==) _     _     = False

Sometimes, you can have Haskell logically write functions that allow a type to be in an existing type class by using deriving (...). Here are some examples of type declarations:

data Bool = False | True
       deriving (Eq, Ord, Bounded, Enum, Read, Show)

data Ordering  =  LT | EQ | GT 
       deriving (Eq, Ord, Bounded, Enum, Read, Show)

data CoinFlip = Heads | Tails
       deriving (Show, Eq, Ord)

data Suit = Clubs | Diamonds | Hearts | Spades
       deriving (Show, Eq, Ord)

data ThermostatSetting = Off 
                       | Cooling 
                       | Heating
       deriving (Show, Eq, Ord)

You can also give different names to types that already exist. For example, you can say that Strings are the same as [Char]. Then, if you input :info String into ghci, you will get type String = [Char]. However, ghci will still identify "Hello" and other strings as [Char].

When using this method to derive Ord, the ordering is determined by the order in which the possible values are written. For example, when we write a new type as such:

data Mystery = A | B
       deriving (Eq, Ord)

The comparison A < B will return True.

Using a type

data ThermostatSetting = Off 
                       | Cooling 
                       | Heating
       deriving (Show, Eq, Ord)

Let's define isRunning :: ThermostatSetting -> Bool

We can use pattern matching!
isRunning Off = False
isRunning Heating = True
isRunning Cooling = True

Or, more simply

isRunning Off = False
isRunning othermode = True

Or don't bother with a named variable at all and use a wildcard name instead.

isRunning Off = False
isRunning _   = True

Note that implementing Eq is NOT required to use pattern matching.

Associated Values

data ThermostatSetting = Off 
                       | CoolTo Int 
                       | HeatTo Int
                       | OutOfService String
       deriving (Show, Eq, Ord)

Let's define isRunning :: Int -> ThermostatSetting -> Bool

We can use pattern matching! Note that when you're using associated values in pattern matching, you need to put parentheses around the whole value, as in (CoolTo t) (otherwise Haskell would think you were defining a function with three arguments).
isRunning _ Off              = False
isRunning _ (OutOfService _) = False
isRunning temp (CoolTo t)    = temp > t
isRunning temp (HeatTo t)    = temp < t

data Crayon = Red | Orange | Yellow | Green 
            | Blue | Purple | Black | White
            | RGB  Int Int Int
       deriving (Show, Ord, Eq)

data Complex = Rect   Float Float
             | Polar  Float Float
       deriving (Show)

Note that in the case of Complex, we don't want to use the default implementations of == or <, because we need to do some actual math to compare rectangular and polar coordinates. Thus we don't use deriving for Eq or Ord.

Associated values should look totally new to you, unless you've worked with something like Swift (enums with associated values), Pascal (variant records/variants), Rust (discriminated/tagged unions), or ML.

C and C++ have a construct that is similar in that it can store one of several data types (union), however, it lacks the "tag" that lets us know which type we have. Associated values can be used by either creating a custom datatype or using the visitor design pattern.

Recursive Types

data ListOfInt = Nil 
               | Cons Int ListOfInt
       deriving (Show, Eq, Ord)

Let's define ihead :: ListOfInt -> Int

ihead Nil = error "empty"
ihead (Cons e _) = e

Recursive Types with Parameters

data ListOf a  = Nil 
               | Cons a (ListOf a)
       deriving (Show, Eq, Ord)

Let's define phead :: ListOf a -> a

The code is exactly the same.
phead Nil = error "empty"
phead (Cons e _) = e

Haskell's Built-in List Type

data [a]       = [] 
               | a : [a]
       deriving (Show, Eq, Ord)

Let's define head :: [a] -> a

head [] = error "empty"
head (e : _) = e

If you want to print this page, you can click to show all the answers.

-- MelissaONeill - 05 Feb 2020

toggleopenShow attachmentstogglecloseHide attachments
Topic attachments
I Attachment Action Size Date Who Comment
elsecss extra.css manage 0.7 K 05 Feb 2020 - 22:13 MelissaONeill CSS to color code samples
Edit | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r2 < r1 | More topic actions
 
Harvey Mudd College computer science
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback