Sie sind auf Seite 1von 13

Haskell Cheat Sheet Strings Enumerations

• "abc" – Unicode string, sugar for


This cheat sheet lays out the fundamental ele-
['a','b','c']. • [1..10] – List of numbers – 1, 2, . . ., 10.
ments of the Haskell language: syntax, keywords
• 'a' – Single character. • [100..] – Infinite list of numbers – 100,
and other elements. It is presented as both an ex- 101, 102, . . . .
ecutable Haskell file and a printable document. • [110..100] – Empty list; ranges only go for-
Multi-line Strings Normally, it is a syntax error
Load the source into your favorite interpreter to wards.
if a string has any actual newline characters. That
play with code samples shown. • [0, -1 ..] – Negative integers.
is, this is a syntax error:
• [-100..-110] – Syntax error; need
string1 = "My long [-100.. -110] for negatives.
Basic Syntax string." • [1,3..100], [-1,3..100] – List from 1 to
100 by 2, -1 to 100 by 4.
Backslashes (‘\’) can “escape” a newline:
Comments In fact, any value which is in the Enum class can be
string1 = "My long \ used:
A single line comment starts with ‘--’ and extends \string." • ['a' .. 'z'] – List of characters – a, b,
to the end of the line. Multi-line comments start
. . ., z.
with ’{-’ and extend to ’-}’. Comments can be The area between the backslashes is ignored.
Newlines in the string must be represented explic- • [1.0, 1.5 .. 2] – [1.0,1.5,2.0].
nested.
itly: • [UppercaseLetter ..] – List of
Comments above function definitions should
GeneralCategory values (from Data.Char).
start with ‘{- |’ and those next to parameter types
string2 = "My long \n\
with ‘-- ^’ for compatibility with Haddock, a sys-
\string."
tem for documenting Haskell code.
That is, string1 evaluates to:
Lists & Tuples
Reserved Words My long string.
• [] – Empty list.
While string2 evaluates to:
The following words are reserved in Haskell. It is • [1,2,3] – List of three numbers.
a syntax error to give a variable or a function one My long • 1 : 2 : 3 : [] – Alternate way to write
of these names. string. lists using “cons” (:) and “nil” ([]).
• "abc" – List of three characters (strings are
• case • import • of Numbers lists).
• class • in • module • 'a' : 'b' : 'c' : [] – List of characters
• data • infix • newtype • 1 – Integer or Floating point (same as "abc").
• deriving • infixl • then • 1.0, 1e10 – Floating point • (1,"a") – 2-element tuple of a number and
• do • infixr • type • 1. – syntax error a string.
• else • instance • where • -1 – sugar for (negate 1) • (head, tail, 3, 'a') – 4-element tuple of
• if • let • 2-1 – sugar for ((-) 2 1) two functions, a number and a character.


c 2009 Justin Bailey. 1 jgbailey@codeslower.com
“Layout” rule, braces and semi-colons. Let Indent the body of the let at least one space Nesting & Capture Nested matching and bind-
from the first definition in the let. If let appears ing are also allowed.
Haskell can be written using braces and semi- on its own line, the body of any definition must
colons, just like C. However, no one does. Instead, appear in the column after the let: data Maybe a = Just a | Nothing
the “layout” rule is used, where spaces represent
scope. The general rule is: always indent. When square x =
the compiler complains, indent more. let x2 = Figure 1: The definition of Maybe
x * x
in x2 Using Maybe we can determine if any choice
Braces and semi-colons Semi-colons termi- was given using a nested match:
nate an expression, and braces represent scope. As can be seen above, the in keyword must also be
They can be used after several keywords: where, in the same column as let. Finally, when multiple anyChoice1 ch =
let, do and of. They cannot be used when defin- definitions are given, all identifiers must appear in case ch of
ing a function body. For example, the below will the same column. Nothing -> "No choice!"
not compile. Just (First _) -> "First!"
Just Second -> "Second!"
square2 x = { x * x; }
Keywords _ -> "Something else."
Haskell keywords are listed below, in alphabetical Binding can be used to manipulate the value
However, this will work fine:
order. matched:
square2 x = result
Case anyChoice2 ch =
where { result = x * x; }
case ch of
case is similar to a switch statement in C# or Java, Nothing -> "No choice!"
Function Definition Indent the body at least but can match a pattern: the shape of the value be- Just score@(First "gold") ->
one space from the function name: ing inspected. Consider a simple data type: "First with gold!"
data Choices = First String | Second | Just score@(First _) ->
square x = "First with something else: "
Third | Fourth
x * x ++ show score
case can be used to determine which choice was _ -> "Not first."
Unless a where clause is present. In that case, in- given:
dent the where clause at least one space from the
function name and any function bodies at least whichChoice ch = Matching Order Matching proceeds from top
case ch of to bottom. If anyChoice1 is reordered as follows,
one space from the where keyword:
First _ -> "1st!" the first pattern will always succeed:
square x = Second -> "2nd!"
anyChoice3 ch =
x2 _ -> "Something else."
case ch of
where x2 = As with pattern-matching in function definitions, _ -> "Something else."
x * x the ‘_’ token is a “wildcard” matching any value. Nothing -> "No choice!"


c 2009 Justin Bailey. 2 jgbailey@codeslower.com
Just (First _) -> "First!" class Flavor a where Data
Just Second -> "Second!" flavor :: a -> String
So-called algebraic data types can be declared as fol-
Notice that the declaration only gives the type lows:
Guards Guards, or conditional matches, can be
signature of the function—no implementation is
used in cases just like function definitions. The data MyType = MyValue1 | MyValue2
given here (with some exceptions, see “Defaults”
only difference is the use of the -> instead of
on page 3). Continuing, we can define several in- MyType is the type’s name. MyValue1 and
=. Here is a simple function which does a case- stances: MyValue are values of the type and are called con-
insensitive string match:
structors. Multiple constructors are separated with
instance Flavor Bool where
strcmp s1 s2 = case (s1, s2) of the ‘|’ character. Note that type and constructor
flavor _ = "sweet"
([], []) -> True names must start with a capital letter. It is a syn-
(s1:ss1, s2:ss2) tax error otherwise.
instance Flavor Char where
| toUpper s1 == toUpper s2 ->
flavor _ = "sour"
strcmp ss1 ss2 Constructors with Arguments The type above
| otherwise -> False Evaluating flavor True gives: is not very interesting except as an enumeration.
_ -> False Constructors that take arguments can be declared,
> flavor True
"sweet" allowing more information to be stored:
Class
While flavor 'x' gives:
data Point = TwoD Int Int
A Haskell function is defined to work on a certain | ThreeD Int Int Int
type or set of types and cannot be defined more > flavor 'x'
Notice that the arguments for each constructor are
than once. Most languages support the idea of "sour"
type names, not constructors. That means this
“overloading”, where a function can have differ-
kind of declaration is illegal:
ent behavior depending on the type of its argu- Defaults Default implementations can be given
ments. Haskell accomplishes overloading through for functions in a class. These are useful when cer- data Poly = Triangle TwoD TwoD TwoD
class and instance declarations. A class defines tain functions can be defined in terms of others in
instead, the Point type must be used:
one or more functions that can be applied to any the class. A default is defined by giving a body
types which are members (i.e., instances) of that to one of the member functions. The canonical ex- data Poly = Triangle Point Point Point
class. A class is analogous to an interface in Java ample is Eq, which defines /= (not equal) in terms
or C#, and instances to a concrete implementation of ==. : Type and Constructor Names Type and con-
of the interface. structor names can be the same, because they will
class Eq a where
A class must be declared with one or more never be used in a place that would cause confu-
(==) :: a -> a -> Bool
type variables. Technically, Haskell 98 only al- sion. For example:
(/=) :: a -> a -> Bool
lows one type variable, but most implementations
(/=) a b = not (a == b) data User = User String | Admin String
of Haskell support so-called multi-parameter type
classes, which allow more than one type variable. Recursive definitions can be created, but an which declares a type named User with two con-
We can define a class which supplies a flavor instance declaration must always implement at structors, User and Admin. Using this type in a
for a given type: least one class member. function makes the difference clear:


c 2009 Justin Bailey. 3 jgbailey@codeslower.com
whatUser (User _) = "normal user." Multiple constructors (of the same type) can use Because seven of these operations are so com-
whatUser (Admin _) = "admin user." the same accessor function for values of the same mon, Haskell provides the deriving keyword
type, but that can be dangerous if the accessor is which will automatically implement the typeclass
Some literature refers to this practice as type pun- not used by all constructors. Consider this rather on the associated type. The seven supported type-
ning. contrived example: classes are: Eq, Read, Show, Ord, Enum, Ix, and
Bounded.
data Con = Con { conValue :: String }
Type Variables Declaring so-called polymorphic Two forms of deriving are possible. The first
| Uncon { conValue :: String }
data types is as easy as adding type variables in is used when a type only derives one class:
| Noncon
the declaration:

data Slot1 a = Slot1 a | Empty1 whichCon con = "convalue is " ++ data Priority = Low | Medium | High
conValue con deriving Show
This declares a type Slot1 with two constructors,
Slot1 and Empty1. The Slot1 constructor can take If whichCon is called with a Noncon value, a run-
an argument of any type, which is represented by time error will occur. The second is used when multiple classes are de-
the type variable a above. Finally, as explained elsewhere, these names rived:
We can also mix type variables and specific can be used for pattern matching, argument cap-
types in constructors: ture and “updating.”
data Alarm = Soft | Loud | Deafening
deriving (Read, Show)
data Slot2 a = Slot2 a Int | Empty2 Class Constraints Data types can be declared
with class constraints on the type variables, but
Above, the Slot2 constructor can take a value of It is a syntax error to specify deriving for any
this practice is generally discouraged. It is gener-
any type and an Int value. other classes besides the six given above.
ally better to hide the “raw” data constructors us-
ing the module system and instead export “smart”
Record Syntax Constructor arguments can be constructors which apply appropriate constraints.
declared either positionally, as above, or using In any case, the syntax used is: Deriving
record syntax, which gives a name to each argu-
ment. For example, here we declare a Contact data (Num a) => SomeNumber a = Two a a
See the section on deriving under the data key-
type with names for appropriate arguments: | Three a a a
word on page 4.
This declares a type SomeNumber which has one
data Contact = Contact { ctName :: String
type variable argument. Valid types are those in
, ctEmail :: String
the Num class. Do
, ctPhone :: String }

These names are referred to as selector or acces- Deriving Many types have common operations The do keyword indicates that the code to follow
sor functions and are just that, functions. They which are tedious to define yet necessary, such as will be in a monadic context. Statements are sepa-
must start with a lowercase letter or underscore the ability to convert to and from strings, compare rated by newlines, assignment is indicated by <-,
and cannot have the same name as another func- for equality, or order in a sequence. These capa- and a let form is introduce which does not re-
tion in scope. Thus the “ct” prefix on each above. bilities are defined as typeclasses in Haskell. quire the in keyword.


c 2009 Justin Bailey. 4 jgbailey@codeslower.com
If and IO if can be tricky when used with let result = file -> do
IO. Conceptually it is no different from an if if exists f <- readFile file
in any other context, but intuitively it is hard to then 1 putStrLn ("The file is " ++
develop. Consider the function doesFileExists else 0 show (length f)
from System.Directory: return result ++ " bytes long.")
doesFileExist :: FilePath -> IO Bool Again, notice where return is. We don’t put it in An alternative syntax uses semi-colons and braces.
the let statement. Instead we use it once at the A do is still required, but indention is unnecessary.
The if statement has this “signature”:
end of the function. This code shows a case example, but the principle
if-then-else :: Bool -> a -> a -> a applies to if as well:

That is, it takes a Bool value and evaluates to some Multiple do’s When using do with if or case, countBytes3 =
other value based on the condition. From the type another do is required if either branch has multi- do
signatures it is clear that doesFileExist cannot be ple statements. An example with if: putStrLn "Enter a filename."
used directly by if: args <- getLine
countBytes1 f =
case args of
wrong fileName = do
[] -> putStrLn "No args given."
if doesFileExist fileName putStrLn "Enter a filename."
file -> do { f <- readFile file;
then ... args <- getLine
putStrLn ("The file is " ++
else ... if length args == 0
show (length f)
-- no 'do'.
That is, doesFileExist results in an IO Bool ++ " bytes long."); }
then putStrLn "No filename given."
value, while if wants a Bool value. Instead, the else
correct value must be “extracted” by running the -- multiple statements require
Export
IO action: -- a new 'do'. See the section on module on page 6.
right1 fileName = do do
exists <- doesFileExist fileName f <- readFile args If, Then, Else
if exists putStrLn ("The file is " ++
show (length f) Remember, if always “returns” a value. It is an
then return 1
else return 0 ++ " bytes long.") expression, not just a control flow statement. This
function tests if the string given starts with a lower
Notice the use of return. Because do puts us “in- And one with case: case letter and, if so, converts it to upper case:
side” the IO monad, we can’t “get out” except
countBytes2 = -- Use pattern-matching to
through return. Note that we don’t have to use
do -- get first character
if inline here—we can also use let to evaluate
putStrLn "Enter a filename." sentenceCase (s:rest) =
the condition and get a value first:
args <- getLine if isLower s
right2 fileName = do case args of then toUpper s : rest
exists <- doesFileExist fileName [] -> putStrLn "No args given." else s : rest


c 2009 Justin Bailey. 5 jgbailey@codeslower.com
-- Anything else is empty string again. This is useful for capturing common por- Module
sentenceCase _ = [] tions of your function and re-using them. Here is
A module is a compilation unit which exports
a silly example which gives the sum of a list of
functions, types, classes, instances, and other
Import numbers, their average, and their median:
modules. A module can only be defined in one
See the section on module on page 6. listStats m = file, though its exports may come from multiple
let numbers = [1,3 .. m] sources. To make a Haskell file a module, just add
total = sum numbers a module declaration at the top:
In mid = head (take (m `div` 2) module MyModule where
See let on page 6. numbers)
Module names must start with a capital letter but
in "total: " ++ show total ++
otherwise can include periods, numbers and un-
", mid: " ++ show mid
Infix, infixl and infixr derscores. Periods are used to give sense of struc-
ture, and Haskell compilers will use them as indi-
See the section on operators on page 11. Deconstruction The left-hand side of a let
cations of the directory a particular source file is,
definition can also destructure its argument, in
but otherwise they have no meaning.
case sub-components are to be accessed. This defi-
Instance The Haskell community has standardized a set
nition would extract the first three characters from
of top-level module names such as Data, System,
See the section on class on page 3. a string
Network, etc. Be sure to consult them for an ap-
firstThree str = propriate place for your own module if you plan
Let let (a:b:c:_) = str on releasing it to the public.

Local functions can be defined within a function


in "Initial three characters are: " ++
using let. The let keyword must always be fol-
show a ++ ", " ++ Imports The Haskell standard libraries are di-
lowed by in. The in must appear in the same col-
show b ++ ", and " ++ vided into a number of modules. The functional-
umn as the let keyword. Functions defined have
show c ity provided by those libraries is accessed by im-
porting into your source file. To import all every-
access to all other functions and variables within Note that this is different than the following, thing exported by a library, just use the module
the same scope (including those defined by let). which only works if the string has three charac- name:
In this example, mult multiplies its argument n ters:
by x, which was passed to the original multiples. import Text.Read
mult is used by map to give the multiples of x up onlyThree str =
Everything means everything: functions, data
to 10: let (a:b:c:[]) = str
types and constructors, class declarations, and
in "The characters given are: " ++
even other modules imported and then exported
multiples x = show a ++ ", " ++ show b ++
by the that module. Importing selectively is ac-
let mult n = n * x ", and " ++ show c
complished by giving a list of names to import.
in map mult [1..10]
For example, here we import some functions from
Of Text.Read:
let “functions” with no arguments are actually
constants and, once evaluated, will not evaluate See the section on case on page 2. import Text.Read (readParen, lex)


c 2009 Justin Bailey. 6 jgbailey@codeslower.com
Data types can imported in a number of ways. We Instance Declarations It must be noted that Exports If an export list is not provided, then
can just import the type and no constructors: instance declarations cannot be excluded from all functions, types, constructors, etc. will be
import: all instance declarations in a module will available to anyone importing the module. Note
import Text.Read (Lexeme)
be imported when the module is imported. that any imported modules are not exported in
Of course, this prevents our module from pattern- this case. Limiting the names exported is accom-
matching on the values of type Lexeme. We can plished by adding a parenthesized list of names
import one or more constructors explicitly: Qualified Imports The names exported by a before the where keyword:
module (i.e., functions, types, operators, etc.) can
import Text.Read (Lexeme(Ident, Symbol)) have a prefix attached through qualified imports. module MyModule (MyType
All constructors for a given type can also be im- This is particularly useful for modules which have , MyClass
ported: a large number of functions having the same name , myFunc1
as Prelude functions. Data.Set is a good example: ...)
import Text.Read (Lexeme(..))
where
We can also import types and classes defined in import qualified Data.Set as Set
the module: The same syntax as used for importing can be
This form requires any function, type, construc- used here to specify which functions, types, con-
import Text.Read (Read, ReadS) tor or other name exported by Data.Set to now structors, and classes are exported, with a few dif-
In the case of classes, we can import the functions be prefixed with the alias (i.e., Set) given. Here is ferences. If a module imports another module, it
defined for a class using syntax similar to import- one way to remove all duplicates from a list: can also export that module:
ing constructors for data types:
removeDups a = module MyBigModule (module Data.Set
import Text.Read (Read(readsPrec , module Data.Char)
Set.toList (Set.fromList a)
, readList)) where
Note that, unlike data types, all class functions are A second form does not create an alias. Instead,
imported unless explicitly excluded. To only im- the prefix becomes the module name. We can import Data.Set
port the class, we use this syntax: write a simple function to check if a string is all import Data.Char
upper case:
import Text.Read (Read()) A module can even re-export itself, which can be
useful when all local definitions and a given im-
import qualified Char
Exclusions If most, but not all, names are to ported module are to be exported. Below we ex-
be imported from a module, it would be tedious port ourselves and Data.Set, but not Data.Char:
to list them all. For that reason, imports can also allUpper str =
be specified via the hiding keyword: all Char.isUpper str module AnotherBigModule (module Data.Set
, module AnotherBigModule)
import Data.Char hiding (isControl where
Except for the prefix specified, qualified imports
, isMark)
support the same syntax as normal imports. The
Except for instance declarations, any type, func- name imported can be limited in the same ways import Data.Set
tion, constructor or class can be hidden. as described above. import Data.Char


c 2009 Justin Bailey. 7 jgbailey@codeslower.com
Newtype Finally, it should be noted that any deriving lName (Person f l ) =
clause which can be attached to a data declaration Person (lower f) (lower l)
While data introduces new values and type just
can also be used when declaring a newtype.
creates synonyms, newtype falls somewhere be- Because type is just a synonym, it cannot declare
tween. The syntax for newtype is quite restricted— multiple constructors the way data can. Type vari-
only one constructor can be defined, and that con- Return ables can be used, but there cannot be more than
structor can only take one argument. Continuing See do on page 4. the type variables declared with the original type.
the above example, we can define a Phone type as That means a synonym like the following is possi-
follows: ble:
Type
newtype Home = H String type NotSure a = Maybe a
This keyword defines a type synonym (i.e., alias).
newtype Work = W String
This keyword does not define a new type, like
data Phone = Phone Home Work but this not:
data or newtype. It is useful for documenting code
As opposed to type, the H and W “values” on but otherwise has no effect on the actual type of type NotSure a b = Maybe a
Phone are not just String values. The typechecker a given function or value. For example, a Person
treats them as entirely new types. That means our data type could be defined as: Note that fewer type variables can be used, which
lowerName function from above would not com- useful in certain instances.
pile. The following produces a type error: data Person = Person String String

where the first constructor argument represents Where


lPhone (Phone hm wk) =
Phone (lower hm) (lower wk) their first name and the second their last. How- Similar to let, where defines local functions and
ever, the order and meaning of the two arguments constants. The scope of a where definition is the
Instead, we must use pattern-matching to get to is not very clear. A type declaration can help: current function. If a function is broken into mul-
the “values” to which we apply lower: tiple definitions through pattern-matching, then
type FirstName = String
lPhone (Phone (H hm) (W wk)) = the scope of a particular where clause only ap-
type LastName = String
Phone (H (lower hm)) (W (lower wk)) plies to that definition. For example, the function
data Person = Person FirstName LastName
result below has a different meaning depending
The key observation is that this keyword does not Because type introduces a synonym, type check- on the arguments given to the function strlen:
introduce a new value; instead it introduces a new ing is not affected in any way. The function lower,
type. This gives us two very useful properties: strlen [] = result
defined as:
• No runtime cost is associated with the new where result = "No string given!"
type, since it does not actually produce new lower s = map toLower s strlen f = result ++ " characters long!"
values. In other words, newtypes are abso- where result = show (length f)
lutely free! which has the type
• The type-checker is able to enforce that com- lower :: String -> String Where vs. Let A where clause can only be de-
mon types such as Int or String are used fined at the level of a function definition. Usu-
in restricted ways, specified by the program- can be used on values with the type FirstName or ally, that is identical to the scope of let defini-
mer. LastName just as easily: tion. The only difference is when guards are being


c 2009 Justin Bailey. 8 jgbailey@codeslower.com
used. The scope of the where clause extends over Pattern matching can extend to nested values. Argument Capture Argument capture is use-
all guards. In contrast, the scope of a let expres- Assuming this data declaration: ful for pattern-matching a value and using it, with-
sion is only the current function clause and guard, out declaring an extra variable. Use an ‘@’ symbol
if any. data Bar = Bil (Maybe Int) | Baz in between the pattern to match and the variable
to bind the value to. This facility is used below to
and recalling the definition of Maybe from page 2 bind the head of the list in l for display, while also
Declarations, Etc. we can match on nested Maybe values when Bil is binding the entire list to ls in order to compute its
present: length:
The following section details rules on function
declarations, list comprehensions, and other areas f (Bil (Just _)) = ... len ls@(l:_) = "List starts with " ++
of the language. f (Bil Nothing) = ... show l ++ " and is " ++
f Baz = ... show (length ls) ++ " items long."
Function Definition len [] = "List is empty!"
Pattern-matching also allows values to be as-
Functions are defined by declaring their name, signed to variables. For example, this function de-
any arguments, and an equals sign: Guards Boolean functions can be used as
termines if the string given is empty or not. If not, “guards” in function definitions along with pat-
square x = x * x the value bound to str is converted to lower case: tern matching. An example without pattern
matching:
All functions names must start with a lowercase toLowerStr [] = []
letter or “_”. It is a syntax error otherwise. toLowerStr str = map toLower str which n
| n == 0 = "zero!"
Note that str above is similer to _ in that it will | even n = "even!"
Pattern Matching Multiple “clauses” of a func-
tion can be defined by “pattern-matching” on the
match anything; the only difference is that the | otherwise = "odd!"
value matched is also given a name.
values of arguments. Here, the the agree function
Notice otherwise – it always evaluates to true and
has four separate cases:
can be used to specify a “default” branch.
n + k Patterns This (sometimes controversial) Guards can be used with patterns. Here is a
-- Matches when the string "y" is given.
pattern-matching facility makes it easy to match function that determines if the first character in a
agree1 "y" = "Great!"
certain kinds of numeric expressions. The idea string is upper or lower case:
-- Matches when the string "n" is given.
is to define a base case (the “n” portion) with a
agree1 "n" = "Too bad."
constant number for matching, and then to define what [] = "empty string!"
-- Matches when string beginning
other matches (the “k” portion) as additives to the what (c:_)
-- with 'y' given.
base case. Here is a rather inefficient way of test- | isUpper c = "upper case!"
agree1 ('y':_) = "YAHOO!"
ing if a number is even or not: | isLower c = "lower case"
-- Matches for any other value given.
| otherwise = "not a letter!"
agree1 _ = "SO SAD." isEven 0 = True
Note that the ‘_’ character is a wildcard and isEven 1 = False Matching & Guard Order Pattern-matching
matches any value. isEven (n + 2) = isEven n proceeds in top to bottom order. Similarly, guard


c 2009 Justin Bailey. 9 jgbailey@codeslower.com
expressions are tested from top to bottom. For ex- Lazy Patterns This syntax, also known as ir- As long as the value x is not actually evaluated,
ample, neither of these functions would be very refutable patterns, allows pattern matches which we’re safe. None of the base types need to look
interesting: always succeed. That means any clause using the at x (see the “_” matches they use), so things will
pattern will succeed, but if it tries to actually use work just fine.
allEmpty _ = False the matched value an error may occur. This is gen- One wrinkle with the above is that we must
allEmpty [] = True erally useful when an action should be taken on provide type annotations in the interpreter or the
the type of a particular value, even if the value isn’t code when using a Nothing constructor. Nothing
alwaysEven n present. has type Maybe a but, if not enough other infor-
| otherwise = False For example, define a class for default values: mation is available, Haskell must be told what a
| n `div` 2 == 0 = True is. Some example default values:
class Def a where
defValue :: a -> a -- Return "Just False"
Record Syntax Normally pattern matching oc-
The idea is you give defValue a value of the right defMB = defValue (Nothing :: Maybe Bool)
curs based on the position of arguments in the
type and it gives you back a default value for that -- Return "Just ' '"
value being matched. Types declared with record
type. Defining instances for basic types is easy: defMC = defValue (Nothing :: Maybe Char)
syntax, however, can match based on those record
names. Given this data type: instance Def Bool where
defValue _ = False List Comprehensions
data Color = C { red
A list comprehension consists of four types of el-
, green instance Def Char where
ements: generators, guards, local bindings, and tar-
, blue :: Int } defValue _ = ' '
gets. A list comprehension creates a list of target
we can match on green only: Maybe is a littler trickier, because we want to get values based on the generators and guards given.
a default value for the type, but the constructor This comprehension generates all squares:
isGreenZero (C { green = 0 }) = True might be Nothing. The following definition would
isGreenZero _ = False work, but it’s not optimal since we get Nothing squares = [x * x | x <- [1..]]
when Nothing is passed in.
x <- [1..] generates a list of all Integer values
Argument capture is possible with this syntax, al-
instance Def a => Def (Maybe a) where and puts them in x, one by one. x * x creates each
though it gets clunky. Continuing the above, we
defValue (Just x) = Just (defValue x) element of the list by multiplying x by itself.
now define a Pixel type and a function to replace
defValue Nothing = Nothing Guards allow certain elements to be excluded.
values with non-zero green components with all
We’d rather get a Just (default value) back in- The following shows how divisors for a given
black:
stead. Here is where a lazy pattern saves us – number (excluding itself) can be calculated. No-
data Pixel = P Color we can pretend that we’ve matched Just x and tice how d is used in both the guard and target
use that to get a default value, even if Nothing is expression.
-- Color value untouched if green is 0 given:
divisors n =
setGreen (P col@(C { green = 0 })) = P col instance Def a => Def (Maybe a) where [d | d <- [1..(n `div` 2)]
setGreen _ = P (C 0 0 0) defValue ~(Just x) = Just (defValue x) , n `mod` d == 0]


c 2009 Justin Bailey. 10 jgbailey@codeslower.com
Local bindings provide new definitions for use in To define a new operator, simply define it as a infix | infixr | infixl precedence op
the generated expression or subsequent genera- normal function, except the operator appears be-
tors and guards. Below, z is used to represent the tween the two arguments. Here’s one which takes where precedence varies from 0 to 9. Op can actu-
minimum of a and b: inserts a comma between two strings and ensures ally be any function which takes two arguments
no extra spaces appear: (i.e., any binary operation). Whether the operator
strange = [(a,z) | a <-[1..3]
is left or right associative is specified by infixl
, b <-[1..3] first ## last =
or infixr, respectively. Such infix declarations
, c <- [1..3] let trim s = dropWhile isSpace
have no associativity.
, let z = min a b (reverse (dropWhile isSpace
, z < c ] Precedence and associativity make many of
(reverse s)))
the rules of arithmetic work “as expected.” For ex-
in trim last ++ ", " ++ trim first
Comprehensions are not limited to numbers. Any ample, consider these minor updates to the prece-
list will do. All upper case letters can be gener- > " Haskell " ## " Curry " dence of addition and multiplication:
ated: Curry, Haskell
infixl 8 `plus1`
ups = plus1 a b = a + b
Of course, full pattern matching, guards, etc. are
[c | c <- [minBound .. maxBound] infixl 7 `mult1`
available in this form. Type signatures are a bit
, isUpper c] mult1 a b = a * b
different, though. The operator “name” must ap-
Or, to find all occurrences of a particular break pear in parentheses:
The results are surprising:
value br in a list word (indexing from 0):
(##) :: String -> String -> String
idxs word br = > 2 + 3 * 5
Allowable symbols which can be used to define
[i | (i, c) <- zip [0..] word 17
operators are:
, c == br] > 2 `plus1` 3 `mult1` 5
# $ % & * + . / < = > ? @ \ ^ | - ~ 25
A unique feature of list comprehensions is that
pattern matching failures do not cause an error; However, there are several “operators” which can- Reversing associativity also has interesting effects.
they are just excluded from the resulting list. not be redefined. They are: <-, -> and =. The last, Redefining division as right associative:
=, cannot be redefined by itself, but can be used as
Operators part of multi-character operator. The “bind” func- infixr 7 `div1`
tion, >>=, is one example. div1 a b = a / b
There are very few predefined “operators” in
Haskell—most that appear predefined are actually We get interesting results:
syntax (e.g., “=”). Instead, operators are simply Precedence & Associativity The precedence
functions that take two arguments and have spe- and associativity, collectively called fixity, of any
operator can be set through the infix, infixr and > 20 / 2 / 2
cial syntactic support. Any so-called operator can 5.0
be applied as a prefix function using parentheses: infixl keywords. These can be applied both to
top-level functions and to local definitions. The > 20 `div1` 2 `div1` 2
3 + 4 == (+) 3 4 syntax is: 20.0


c 2009 Justin Bailey. 11 jgbailey@codeslower.com
Currying This can be taken further. Say we want to write Which produces quite different results:
a function which only changes upper case letters.
In Haskell, functions do not have to get all of their
We know the test to apply, isUpper, but we don’t > onLeft "foo" "bar"
arguments at once. For example, consider the
want to specify the conversion. That function can "barfoo"
convertOnly function, which only converts certain > onRight "foo" "bar"
be written as:
elements of string depending on a test: "foobar"
convertUpper = convertOnly isUpper
convertOnly test change str =
map (\c -> if test c which has the type signature: “Updating” values and record syntax
then change c convertUpper :: (Char -> Char) Haskell is a pure language and, as such, has no
else c) str -> String -> String mutable state. That is, once a value is set it never
changes. “Updating” is really a copy operation,
Using convertOnly, we can write the l33t func- That is, convertUpper can take two arguments.
with new values in the fields that “changed.” For
tion which converts certain letters to numbers: The first is the conversion function which converts
example, using the Color type defined earlier, we
individual characters and the second is the string
l33t = convertOnly isL33t toL33t can write a function that sets the green field to
to be converted.
where A curried form of any function which takes
zero easily:
isL33t 'o' = True multiple arguments can be created. One way to noGreen1 (C r _ b) = C r 0 b
isL33t 'a' = True think of this is that each “arrow” in the function’s
-- etc. signature represents a new function which can be The above is a bit verbose and can be rewriten us-
isL33t _ = False created by supplying one more argument. ing record syntax. This kind of “update” only sets
toL33t 'o' = '0' values for the field(s) specified and copies the rest:
toL33t 'a' = '4'
Sections Operators are functions, and they can
-- etc. noGreen2 c = c { green = 0 }
be curried like any other. For example, a curried
toL33t c = c
version of “+” can be written as: Here we capture the Color value in c and return a
Notice that l33t has no arguments specified. add10 = (+) 10 new Color value. That value happens to have the
Also, the final argument to convertOnly is not same value for red and blue as c and it’s green
given. However, the type signature of l33t tells However, this can be unwieldy and hard to read. component is 0. We can combine this with pattern
the whole story: “Sections” are curried operators, using parenthe- matching to set the green and blue fields to equal
ses. Here is add10 using sections: the red field:
l33t :: String -> String
add10 = (10 +) makeGrey c@(C { red = r }) =
That is, l33t takes a string and produces a string. c { green = r, blue = r }
The supplied argument can be on the right or left,
It is a “constant”, in the sense that l33t always
which indicates what position it should take. This
returns a value that is a function which takes a Notice we must use argument capture (“c@”) to
is important for operations such as concatenation:
string and produces a string. l33t returns a “cur- get the Color value and pattern matching with
ried” form of convertOnly, where only two of its onLeft str = (++ str) record syntax (“C { red = r}”) to get the inner
three arguments have been supplied. onRight str = (str ++) red field.


c 2009 Justin Bailey. 12 jgbailey@codeslower.com
Anonymous Functions to later. Writing the type signatures on all Type Annotations Sometimes Haskell cannot
top-level functions is considered very good determine what type is meant. The classic demon-
An anonymous function (i.e., a lambda expression
form. stration of this is the so-called “show . read”
or lambda for short), is a function without a name.
problem:
They can be defined at any time like so: Specialization—Typeclasses allow functions with
overloading. For example, a function to canParseInt x = show (read x)
\c -> (c, c)
negate any list of numbers has the signature: Haskell cannot compile that function because it
which defines a function which takes an argument
negateAll :: Num a => [a] -> [a] does not know the type of x. We must limit the
and returns a tuple containing that argument in
type through an annotation:
both positions. They are useful for simple func-
However, for efficiency or other reasons you
tions which don’t need a name. The following canParseInt x = show ((read x) :: Int)
may only want to allow Int types. You
determines if a string has mixed case (or is all Annotations have the same syntax as type signa-
would accomplish that with a type signa-
whitespace): tures, but may adorn any expression.
ture:
mixedCase str =
all (\c -> isSpace c || negateAll :: [Int] -> [Int] Unit
isLower c || Type signatures can appear on top-level func- () – “unit” type and “unit” value. The value and
isUpper c) str tions and nested let or where definitions. Gen- type that represents no useful information.
Of course, lambdas can be the returned from func- erally this is useful for documentation, although
tions too. This classic returns a function which in some cases they are needed to prevent poly-
will then multiply its argument by the one origi- morphism. A type signature is first the name of Contributors
nally given: the item which will be typed, followed by a ::,
My thanks to those who contributed patches and
followed by the types. An example of this has al-
multBy n = \m -> n * m useful suggestions: Dave Bayer, Elisa Firth, Cale
ready been seen above.
Gibbard, Stephen Hicks, Kurt Hutchinson, Johan
For example: Type signatures do not need to appear directly
Kiviniemi, Adrian Neumann, Barak Pearlmutter,
above their implementation. They can be specified
> let mult10 = multBy 10 Lanny Ripple, Markus Roberts, Holger Siegel, Leif
anywhere in the containing module (yes, even be-
> mult10 10 Warner, and Jeff Zaroyko.
low!). Multiple items with the same signature can
100
also be defined together:
pos, neg :: Int -> Int Version
Type Signatures
Haskell supports full type inference, meaning in ... This is version 1.11. The source can
most cases no types have to be written down. Type be found at GitHub (http://github.com/
pos x | x < 0 = negate x m4dc4p/cheatsheet). The latest released ver-
signatures are still useful for at least two reasons. | otherwise = x sion of the PDF can be downloaded from
Documentation—Even if the compiler can figure http://cheatsheet.codeslower.com. Visit
out the types of your functions, other pro- neg y | y > 0 = negate y CodeSlower.com (http://blog.codeslower.
grammers or even yourself might not be able | otherwise = y com/) for other projects and writings.


c 2009 Justin Bailey. 13 jgbailey@codeslower.com

Das könnte Ihnen auch gefallen