February 2021
CharUnicode characters
IntFixed-precision integers
IntegerArbitrary-precision integers
FloatSingle-precision floating point
DoubleDouble-precision floating point
Functions with multiple arguments take one argument at a time:
bar :: Int -> (Int -> Int)
bar x y = sq x + incr y
-- alternative using lambda expressions
bar' :: Int -> (Int -> Int)
bar' = \x -> \y -> sq x + incr yConvention: functions application associates to the left and the type arrow associates to the right, e.g.
f arg1 arg2 arg3\(\equiv\) (f arg1) arg2) arg3
Int -> Int -> Int\(\equiv\) Int -> (Int -> Int)
Note also that
Int -> (Int -> Int)\(\neq\) (Int -> Int) -> Int
f $ argapply function f to argument arg
f . gcompose functions f and g
E.g.
Not special syntax, just definitions in the Prelude.
Eager evaluation (strict):
Lazy evaluation (non-strict):
sq x = x*x
incr x = x+1
foo x = sq (incr x)
foo 1
= sq (incr 1)
= (incr 1)*(incr 1)
= (1+1) * (incr 1)
= 2 * (incr 1)
= 2 * 2 -- re-used result
= 4if_then_else :: Bool -> a -> a -> a
if_then_else True x _ = x
if_then_else False _ y = y
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)Haskell employs non-strict evaluation by default, but allows choosing strict evaluation when required.
f $! argstrict application of function f to argument arg
> (\x -> 42) 1
42
> (\x -> 42) undefined
42
> (\x -> 42) $! undefined
-- ***Exception: Prelude.undefinedA -> Bfunctions
(A,B)pairs (also triples, quadruples, etc.)
[A]homogeneous lists (e.g. same type for every element)
> fst ("Hello", 23)
"Hello"
> snd ("Hello", 23)
23
> 1:2:3:[] -- list constructors
[1,2,3]
> tail [1,2,3]
[2,3]
> head (tail [1..]) -- infinite list
2
> length [1..]
-- error: does not terminate> [x | x <-[1..10], x`mod`3 == 0]
-- list compreension
[3, 6, 9]
> let fs = [\x->x, \x->x+1, \x->2*x]
> fs -- type error: can't show functions
> (fs!!1) 42 -- but we can use it
43
> [f 42 | f <- fs]
[42, 43, 84]equivalent to
Data declarations define new data types:
data Bool = False | True
data Color = Red | Green | Blue
data Result = OK | Error String
data IntList = Empty | Cons Int IntListdata Point = Point Float Float
-- `Point' is a type *and* a constructor
-- (namespaces are separate)
-- make a point
point :: Float -> Float -> Point
point x y = Point x y
-- get coordinates
xcoord, ycoord :: Point -> Float
xcoord (Point x _) = x
ycoord (Point _ y) = yPoint is isomorphic to a pair of coordinatesnewtype Point = Point (Float,Float)
point :: Float -> Float -> Point
point x y = Point (x,y)
xcoord :: Point -> Float
xcoord (Point (x,_)) = xNewtypes allow overloading — more about this later.
We can name the constructors fields explicitly using record syntax:
E.g. the following represent the same value:
> let p = Person "pedro" 44
> age p
44
> let p' = p { age = 45 }
> age p'
45
By default, constructor fields are lazy:
data Point = Point { xcoord :: Float
, ycoord :: Float }
> let p = Point 1.0 undefined
> xcoord p
1.0
> ycoord p
-- ***Exception: Prelude.undefinedWe can use bang annotations (!) to make fields strict:
data Point = Point { xcoord :: !Float
, ycoord :: !Float }
> let p = Point 1.0 undefined
> xcoord p
-- ***Exception: Prelude.undefinedUseful for improving time and space performance of computationally-intense programs (e.g. numerical algorithms).
A function that accepts values of many types is called “polymorphic” (from Greek polumorphos: having many forms).
use the same implementation uniformly to many types (e.g. length)
use the same name for distinct implementations depending on the types (e.g. overloading + for Int and Float)
Haskell supports these two kinds of polymorphism using type quantification and type classes.
What type should assign to the identity function?
id :: Int -> Int -- specific types
id :: Float -> Float
id :: Char -> Char
id :: a -> a -- most general typeThe type variable a is implicitly quantified
We can write the quantifier explicitly:
a with Int, Float, etc.Float -> Int is not an instance
We can also use type variables in type declarations:
type Pair a = (a,a)
data Maybe a = Nothing | Just a
data Either a b = Left a | Right b
data List a = Empty | Cons a (List a)Just 42 :: Maybe Int
Nothing :: Maybe Int
Nothing :: Maybe a -- most general
Left 42 :: Either Int Char
Left 42 :: Either Int b -- most general
Cons 1 (Cons 2 Empty) :: List Int
Empty :: List Int
Empty :: List a -- most general> 1 + 2
3
> 1.5 + 2.0
3.5
(+) :: Int -> Int -> Int
(+) :: Float -> Float -> Float
(+) :: Num a => a -> a -> a -- most general(+) :: a -> a -> a provided that a is a numberNum is a typeclass; a is a (quantified) type variableNum a => ... is a class constraintEqequality ==, /=
Ordtotal ordering <, <=, >, >=
Show, Readconversion to/from strings
Enumenumerable types (i.e. isomorphic to integer ranges)
Boundedbounded types (i.e. with lower and upper bounds)
Num, Fractional, Integral, Floating…numbers of various kinds
foo x = if x>0 then 2*x else x
foo :: Int -> Int -- OK
foo :: Float -> Float -- OK
foo :: a -> a -- wrong
foo :: Num a => a -> a -- wrong
foo :: (Ord a, Num a) => a -> a -- most generalType signatures may be necessary to avoid ambiguity:
-- show :: Show a => a -> String
-- read :: Read a => String -> a
foo :: String -> String
foo txt = show (read txt)
-- ERROR: The type variable is ambiguous
foo' :: String -> String
foo' txt = show (read txt :: Int) -- OK/= is the negation of == and vice-versaThe standard Prelude defines equality for basic types.
Instances for structured types require equality of the components.
instance (Eq a, Eq b) => Eq (a,b) where
(x,y) == (x',y') = x==x' && y==y'
instance Eq a => Eq [a] where
[] == [] = True
(x:xs) == (x':xs') = x==x' && xs==xs'
_ == _ = FalseNote that == is used for diferent types above.
We can define new instances for algebraic data types.
data Person = Person String Int
instance Eq Person where
Person name age == Person name' age'
= name==name' && age==age'Structural equality can be derived automatically:
Eq instance for function typesclass Eq a => Ord a where
(<) :: a -> a -> Bool
(>=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(<=) :: a -> a -> Bool
... -- extra methods omittedOrd is a sub-class of Eq: every type in Ord be in Eq — not vice-versaclass Num a where
(+) :: a -> a -> a
(*) :: a -> a -> a
(-) :: a -> a -> a
negate :: a -> a
fromInteger :: Integer -> a
...No division operation: there are separate classes for fractional and integral divisions.
Numeric literals are also overloaded.
1 can be any number and 0.5 any fractional number:
However there is no automatic coercion.
avg :: [Float] -> Float
avg xs = sum xs / length xs -- TYPE ERROR
-- `length` gives an Int
-- but (/) requires a FractionalWe can use explicit fromIntegral:
The type of the fromIntegral is
Example:
avg xs = sum xs / fromIntegral(length xs)
-- ^^^^^^ ^^^^^^^^^^
-- Float Int
-- fromIntegral :: Int -> FloatHaskell programs and libraries are organized in modules.
NB: the filename should match the module name.
The entry point for a complete program is a value main in a module Main.
NB: main must have the type IO (); we will discuss I/O when we talk about monads.
Modules can be used to separate namespaces.
module Main where
import A
import B
main = print (A.foo, B.foo)
module A where
foo = 1
module B where
foo = 'a'We can use qualified imports to avoid name colision:
module Main where
import qualified Data.Map as M
import qualified Data.Set as S
foo = S.fromList [1,2,3]
bar = M.fromList [('a',1), ('b',2)]Modules can also hide details by using explicit export lists; e.g. implementing an ADT:
module Queue (
Queue, -- export types
enqueue, -- and operations
dequeue,
...
-- but no constructors
) where
data Queue = ... -- implementation The “de-facto” compiler for the Haskell 2010 standard and plus many extensions.
ghcoptimizing compiler including profiling, FFI to C, etc.
ghcithe interactive environment (read-eval-print loop)
A build-system and source package-management system https://www.haskell.org/cabal/
A central archive of open source Cabal packages: http://hackage.haskell.org/
Many Linux distributions have pre-built packages for Haskell development tools.
(Ubuntu/Debian: haskell-platform and libghc-*.)
Advantages
Disadvantages
Haskell mode avaliable from most Linux distributions
Many more choices: https://wiki.haskell.org/IDEs.
a Haskell API search engine
a tool that gives suggestions on how to improve your Haskell code