Skip to content

Haskell Basics

Operators

Precedence & Associativity

-- (infix | infixr | infixl) precedence op
-- precedence varies from 0 to 9
infixl 8 `plus1`
plus1 a b = a + b
infixl 7 `mult1`

{- |
>>> 2 + 3 * 5
17
>>> 2 `plus1` 3 `mult1` 5
25
-}

-- reversing associativity
infixr 7 `div1`
div1 a b = a / b

{- |
>>> 20 / 2 / 2
5.0
>>> 20 `div1` 2 `div1` 2
20.0
-}
  • -> is right associative

List comprehension

  • each successive generator refines the results of the previous generator.
-- boolean guards
take 10 [(i,j) | i <- [1..], j <- [i..j-1], gcd i j == 1]
[(x,y) | x <- [1..5], even x, y <- [2..x]]

-- local let declarations
take 10 [(i,j) | i <- [1..], let k = i*i, j <- [1..k]]

Function Definition

  • A where clause can only be defined at the level of a function definition.

Pattern Matching

  • Multiple "clauses" of a function can be defined by "pattern-matching" on the values of arguments.
-- Case
data Choices = First String | Second | Third | Fourth
whichChoice ch =
    case ch of
        First _ -> "1st"
        Second -> "2nd"
        _ -> "Something else"

-- Nested
-- data Maybe a = Just a | Nothing
anyChoice1 ch = 
    case ch of
        Nothing -> "No choice"
        Just (First _) -> "First"
        _ -> "Something else"

-- Binding
anyChoice2 ch =
    case ch of
        Nothing -> "No choice"
        Just score@(First "gold") -> "First with gold"
        Just score@(First _) -> "First with " ++ show score
        _ -> "Not first"

Guards

  • Boolean functions can be used as "guards" in function definitions along with pattern matching.
-- without pattern matching
which n
    | n == 0 = "zeros"
    | even n = "even"
    | otherwise = "odd"

-- Guards can be used with patterns
what [] = "empty string"
what (c:_)
    | isUpper c = "upper case"
    | isLower c = "lower case"
    | otherwise = "not a letter"
  • Guards, or conditional matches, can be used in cases just like function definitions. The only difference is the use of -> instead of =.
strcmp s1 s2 = case (s1, s2) of
    ([], []) -> True
    (s1:ss1, s2:ss2)
        | toUpper s1 == toUpper s2 ->
            strcmp ss1 ss2
        | otherwise -> False
    _ -> False

Algebraic Data Type

Constructor

  • Difference between the type constructor and the data constructor - Each type can have only one type constructor. (name of the type) - Each type can have multiple data constructors. (used to construct an instance of a type)
  • Constructors that take arguments can be declared
  • Notice that the arguments for each constructors are type names, not constructors.
-- a sum of products
data Point = Point2D Double Double | Point3D Double Double Double

data Poly = Triangle Point Point Point

Type Variables

  • declaring polymorphic data types
-- `Slot1` can take any type 
data Slot1 a = Slot1 a | Empty1
-- mix type variables and specific types
data Slot2 a = Slot2 a Int | Empty2

Record

  • gives a name to each argument
data Student = Student { id::Int, name::String, mark::Int }

{- |
>>> s1 = Student 123 "john" 1
>>> name s1 == "john"
-}

Deriving

  • automatically implement the typeclass on the associated type.
  • The seven supported typeclasses are: Eq, Read, Show, Ord, Enum, Ix, and Bounded

Typeclass

  • A typeclass defines a set of methods that is shared across multiple types.[^4]
  • A type has an instance of a typeclass if it implements the methods of that typeclass.
  • Basic typeclasses: Eq, Ord, Show, Read, Num
  • Typeclassopedia - HaskellWiki
-- :info Eq
class Eq a where
-- `Eq` is a superclass of `Ord`
class Eq a => Ord a where

data Pokemon = Pokemon { pokedexId :: Int, name :: String }
-- define an `Eq` instance for the `Pokemon` typeclass
instance Eq Pokemon where
    pokemon1 == pokemon2 = pokedexId pokemon1 == pokedexId pokemon2

Foldable

  • One can view a right fold as replacing the nil at the end of the list with a specific value, and each cons with a specific other function.[^5] - foldr (:) [] is the identity function on lists.
  • foldl' enforces weak head normal form in each step, thus preventing a thunk from being accumulated.
foldr :: (a -> b -> b) -> b -> [a] -> b
foldl :: (b -> a -> b) -> b -> [a] -> b

Monoid

  • a monoid is a structure that consists of a set of arguments and a binary operation on that set.
  • a monoid satisfies the following properties: - There is an identity element that "doesn't do anything". - Associativity.
-- associativity
(x <> y) <> z = x <> (y <> z)
-- left identity
mempty <> x = x
-- right identity
x <> mempty = x
  • Monoid has three methods - mempty is a value that doesn't impact the result when used together with the binary operation. - mappend (or <>) is a function that puts two monoids together. - mconcat is a function that reduces a list of monoids into one value.
import Data.Monoid

-- `Semigroup` is a superclass of `Monoid`
instance Semigroup Move where
    Move x1 y1 <> Move x2 y2 = Move (x1 + x2) (y1 + y2)

instance Monoid Move where
    mempty = Move 0 0

{- |
>>> stay = mempty :: Move
>>> moves = [Move x y | x <- [0..20], y <- [0..20]]
>>> mconcat moves
-}

Monad

  • Monad Law, a monad is a monoid
-- associativity
(m >>= g) >>= h = m >>= (\x -> g x >>= h)
-- left identity
return a >>= h = h a
-- right identity
m >>= return = m

fmap f xs = xs >>= return . f

do keyword

  • The result of a monadic computation can be "assigned" to a variable using a left arrow <-. Then using that variable in a subsequent monadic computation automatically performs the binding.[^6]
foo a = return a >>= (\b -> (f b >>= (\c -> g b c)))
-- in do-notation
foo a = do { b <- return a; c <- f b; g b c }
-- in layout style
foo a = do
    b <- return a
    c <- f b
    g b c

IO monad

  • Each IO function produces a monadic IO action that takes a Universe as input and returns another Universe as output.
  • The do block glues together these actions in such a way that the Universe produced by one action becomes the input to the next action.[^2]

Every I/O action returns a value. In the type system, the return value is tagged with IO type, distinguishing actions from other values. For example, the type of the function getChar is:[^1]

getChar :: IO Char

[^1]: A Gentle Introduction to Haskell: IO

[^2]: Basics of Haskell - School of Haskell | School of Haskell

[^4]: Introduction to Haskell Typeclasses

[^5]: Fetching Title#jmxy

[^6]: All About Monads - HaskellWiki