Haskell Cheat Sheet Strings ? Haskell Cheat Sheet ... two functions, a number and a character. ...

Download Haskell Cheat Sheet Strings ? Haskell Cheat Sheet ... two functions, a number and a character. ...

Post on 19-Aug-2018

215 views

Category:

Documents

3 download

TRANSCRIPT

Haskell Cheat SheetThis cheat sheet lays out the fundamental elementsof the Haskell language: syntax, keywords andother elements. It is presented as both an ex-ecutable Haskell file and a printable document.Load the source into your favorite interpreter toplay with code samples shown.Basic SyntaxCommentsA single line comment starts with -- and extendsto the end of the line. Multi-line comments startwith {- and extend to -}. Comments can benested.Comments above function definitions shouldstart with {- | and those next to parameter typeswith -- ^ for compatibility with Haddock1, a sys-tem for documenting Haskell code.Reserved WordsThe following words are reserved in Haskell. It is asyntax error to give a variable or a function one ofthese names. case class data deriving do else if import in infix infixl infixr instance let of module newtype then type whereStrings "abc" Unicode string, sugar for['a','b','c']. 'a' Single character.Multi-line Strings Normally, it is a syntax errorif a string has any actual newline characters. Thatis, this is a syntax error:string1 = "My longstring."Backslashes (\) can escape a newline:string1 = "My long \\string."The area between the backslashes is ignored. New-lines in the string must be represented explicitly:string2 = "My long \n\\string."That is, string1 evaluates to:My long string.While string2 evaluates to:My longstring.Numbers 1 Integer or Floating point 1.0, 1e10 Floating point 1. syntax error -1 sugar for (negate 1) 2-1 sugar for ((-) 2 1)Enumerations [1..10] List of numbers 1, 2, . . ., 10. [100..] Infinite list of numbers 100, 101,102, . . . . [110..100] Empty list; ranges only go for-wards. [0, -1 ..] Negative integers. [-100..-110] Syntax error; need[-100.. -110] for negatives. [1,3..100], [-1,3..100] List from 1 to100 by 2, -1 to 100 by 4.In fact, any value which is in the Enum class can beused: ['a' .. 'z'] List of characters a, b,. . ., z. [1.0, 1.5 .. 2] [1.0,1.5,2.0]. [UppercaseLetter ..] List ofGeneralCategory values (from Data.Char).Lists & Tuples [] Empty list. [1,2,3] List of three numbers. 1 : 2 : 3 : [] Alternate way to write listsusing cons (:) and nil ([]). "abc" List of three characters (strings arelists). 'a' : 'b' : 'c' : [] List of characters(same as "abc"). (1,"a") 2-element tuple of a number and astring. (head, tail, 3, 'a') 4-element tuple oftwo functions, a number and a character.1http://haskell.org/haddock/c 2009 Justin Bailey. 1 jgbailey@codeslower.comhttp://haskell.org/haddock/mailto:jgbailey@codeslower.comLayout rule, braces and semi-colons.Haskell can be written using braces and semi-colons, just like C. However, no one does. Instead,the layout rule is used, where spaces representscope. The general rule is: always indent. Whenthe compiler complains, indent more.Braces and semi-colons Semi-colons termi-nate an expression, and braces represent scope.They can be used after several keywords: where,let, do and of. They cannot be used when defin-ing a function body. For example, the below willnot compile.square2 x = { x * x; }However, this will work fine:square2 x = resultwhere { result = x * x; }Function Definition Indent the body at leastone space from the function name:square x =x * xUnless a where clause is present. In that case, in-dent the where clause at least one space from thefunction name and any function bodies at least onespace from the where keyword:square x =x2where x2 =x * xLet Indent the body of the let at least one spacefrom the first definition in the let. If let appearson its own line, the body of any definition mustappear in the column after the let:square x =let x2 =x * xin x2As can be seen above, the in keyword must also bein the same column as let. Finally, when multipledefinitions are given, all identifiers must appear inthe same column.KeywordsHaskell keywords are listed below, in alphabeticalorder.Casecase is similar to a switch statement in C# or Java,but can match a pattern: the shape of the value be-ing inspected. Consider a simple data type:data Choices = First String | Second |Third | Fourthcase can be used to determine which choice wasgiven:whichChoice ch =case ch ofFirst _ -> "1st!"Second -> "2nd!"_ -> "Something else."As with pattern-matching in function definitions,the _ token is a wildcard matching any value.Nesting & Capture Nested matching and bind-ing are also allowed.data Maybe a = Just a | NothingFigure 1: The definition of MaybeUsing Maybe we can determine if any choice wasgiven using a nested match:anyChoice1 ch =case ch ofNothing -> "No choice!"Just (First _) -> "First!"Just Second -> "Second!"_ -> "Something else."Binding can be used to manipulate the valuematched:anyChoice2 ch =case ch ofNothing -> "No choice!"Just score@(First "gold") ->"First with gold!"Just score@(First _) ->"First with something else: "++ show score_ -> "Not first."Matching Order Matching proceeds from top tobottom. If anyChoice1 is reordered as follows, thefirst pattern will always succeed:anyChoice3 ch =case ch of_ -> "Something else."Nothing -> "No choice!"c 2009 Justin Bailey. 2 jgbailey@codeslower.commailto:jgbailey@codeslower.comJust (First _) -> "First!"Just Second -> "Second!"Guards Guards, or conditional matches, can beused in cases just like function definitions. The onlydifference is the use of the -> instead of =. Hereis a simple function which does a case-insensitivestring match:strcmp s1 s2 = case (s1, s2) of([], []) -> True(s1:ss1, s2:ss2)| toUpper s1 == toUpper s2 ->strcmp ss1 ss2| otherwise -> False_ -> FalseClassA Haskell function is defined to work on a certaintype or set of types and cannot be defined morethan once. Most languages support the idea ofoverloading, where a function can have differentbehavior depending on the type of its arguments.Haskell accomplishes overloading through classand instance declarations. A class defines oneor more functions that can be applied to any typeswhich are members (i.e., instances) of that class. Aclass is analogous to an interface in Java or C#, andinstances to a concrete implementation of the inter-face.A class must be declared with one or more typevariables. Technically, Haskell 98 only allows onetype variable, but most implementations of Haskellsupport so-called multi-parameter type classes, whichallow more than one type variable.We can define a class which supplies a flavor fora given type:class Flavor a whereflavor :: a -> StringNotice that the declaration only gives the typesignature of the functionno implementation isgiven here (with some exceptions, see Defaultson page 3). Continuing, we can define several in-stances:instance Flavor Bool whereflavor _ = "sweet"instance Flavor Char whereflavor _ = "sour"Evaluating flavor True gives:> flavor True"sweet"While flavor 'x' gives:> flavor 'x'"sour"Defaults Default implementations can be givenfor functions in a class. These are useful when cer-tain functions can be defined in terms of others inthe class. A default is defined by giving a body toone of the member functions. The canonical exam-ple is Eq, which defines /= (not equal) in terms of==. :class Eq a where(==) :: a -> a -> Bool(/=) :: a -> a -> Bool(/=) a b = not (a == b)Recursive definitions can be created, but aninstance declaration must always implement atleast one class member.DataSo-called algebraic data types can be declared as fol-lows:data MyType = MyValue1 | MyValue2MyType is the types name. MyValue1 andMyValue are values of the type and are called con-structors. Multiple constructors are separated withthe | character. Note that type and constructornames must start with a capital letter. It is a syntaxerror otherwise.Constructors with Arguments The type aboveis not very interesting except as an enumeration.Constructors that take arguments can be declared,allowing more information to be stored:data Point = TwoD Int Int| ThreeD Int Int IntNotice that the arguments for each constructor aretype names, not constructors. That means this kindof declaration is illegal:data Poly = Triangle TwoD TwoD TwoDinstead, the Point type must be used:data Poly = Triangle Point Point PointType and Constructor Names Type and con-structor names can be the same, because they willnever be used in a place that would cause confu-sion. For example:data User = User String | Admin Stringwhich declares a type named User with two con-structors, User and Admin. Using this type in afunction makes the difference clear:c 2009 Justin Bailey. 3 jgbailey@codeslower.commailto:jgbailey@codeslower.comwhatUser (User _) = "normal user."whatUser (Admin _) = "admin user."Some literature refers to this practice as type pun-ning.Type Variables Declaring so-called polymorphicdata types is as easy as adding type variables inthe declaration:data Slot1 a = Slot1 a | Empty1This declares a type Slot1 with two constructors,Slot1 and Empty1. The Slot1 constructor can takean argument of any type, which is represented bythe type variable a above.We can also mix type variables and specifictypes in constructors:data Slot2 a = Slot2 a Int | Empty2Above, the Slot2 constructor can take a value ofany type and an Int value.Record Syntax Constructor arguments can bedeclared either positionally, as above, or usingrecord syntax, which gives a name to each argu-ment. For example, here we declare a Contact typewith names for appropriate arguments:data Contact = Contact { ctName :: String, ctEmail :: String, ctPhone :: String }These names are referred to as selector or accessorfunctions and are just that, functions. They muststart with a lowercase letter or underscore and can-not have the same name as another function inscope. Thus the ct prefix on each above. Mul-tiple constructors (of the same type) can use thesame accessor function for values of the same type,but that can be dangerous if the accessor is not usedby all constructors. Consider this rather contrivedexample:data Con = Con { conValue :: String }| Uncon { conValue :: String }| NonconwhichCon con = "convalue is " ++conValue conIf whichCon is called with a Noncon value, a runtimeerror will occur.Finally, as explained elsewhere, these namescan be used for pattern matching, argument cap-ture and updating.Class Constraints Data types can be declaredwith class constraints on the type variables, butthis practice is generally discouraged. It is gener-ally better to hide the raw data constructors us-ing the module system and instead export smartconstructors which apply appropriate constraints.In any case, the syntax used is:data (Num a) => SomeNumber a = Two a a| Three a a aThis declares a type SomeNumber which has onetype variable argument. Valid types are those inthe Num class.Deriving Many types have common operationswhich are tedious to define yet necessary, such asthe ability to convert to and from strings, comparefor equality, or order in a sequence. These capabil-ities are defined as typeclasses in Haskell.Because seven of these operations are so com-mon, Haskell provides the deriving keywordwhich will automatically implement the typeclasson the associated type. The seven supported type-classes are: Eq, Read, Show, Ord, Enum, Ix, andBounded.Two forms of deriving are possible. The first isused when a type only derives one class:data Priority = Low | Medium | Highderiving ShowThe second is used when multiple classes are de-rived:data Alarm = Soft | Loud | Deafeningderiving (Read, Show)It is a syntax error to specify deriving for any otherclasses besides the six given above.DerivingSee the section on deriving under the data key-word on page 4.DoThe do keyword indicates that the code to followwill be in a monadic context. Statements are sepa-rated by newlines, assignment is indicated by If and IO if can be tricky when used withIO. Conceptually it is no different from an if inany other context, but intuitively it is hard to de-velop. Consider the function doesFileExists fromSystem.Directory:doesFileExist :: FilePath -> IO BoolThe if statement has this signature:if-then-else :: Bool -> a -> a -> aThat is, it takes a Bool value and evaluates to someother value based on the condition. From the typesignatures it is clear that doesFileExist cannot beused directly by if:wrong fileName =if doesFileExist fileNamethen ...else ...That is, doesFileExist results in an IO Bool value,while if wants a Bool value. Instead, the correctvalue must be extracted by running the IO ac-tion:right1 fileName = doexists -- Anything else is empty stringsentenceCase _ = []ImportSee the section on module on page 6.InSee let on page 6.Infix, infixl and infixrSee the section on operators on page 11.InstanceSee the section on class on page 3.LetLocal functions can be defined within a function us-ing let. The let keyword must always be followedby in. The in must appear in the same column asthe let keyword. Functions defined have access toall other functions and variables within the samescope (including those defined by let). In this ex-ample, mult multiplies its argument n by x, whichwas passed to the original multiples. mult is usedby map to give the multiples of x up to 10:multiples x =let mult n = n * xin map mult [1..10]let functions with no arguments are actuallyconstants and, once evaluated, will not evaluateagain. This is useful for capturing common por-tions of your function and re-using them. Here is asilly example which gives the sum of a list of num-bers, their average, and their median:listStats m =let numbers = [1,3 .. m]total = sum numbersmid = head (take (m `div` 2)numbers)in "total: " ++ show total ++", mid: " ++ show midDeconstruction The left-hand side of a let def-inition can also destructure its argument, in casesub-components are to be accessed. This definitionwould extract the first three characters from a stringfirstThree str =let (a:b:c:_) = strin "Initial three characters are: " ++show a ++ ", " ++show b ++ ", and " ++show cNote that this is different than the following, whichonly works if the string has three characters:onlyThree str =let (a:b:c:[]) = strin "The characters given are: " ++show a ++ ", " ++ show b ++", and " ++ show cOfSee the section on case on page 2.ModuleA module is a compilation unit which exports func-tions, types, classes, instances, and other modules.A module can only be defined in one file, thoughits exports may come from multiple sources. Tomake a Haskell file a module, just add a moduledeclaration at the top:module MyModule whereModule names must start with a capital letter butotherwise can include periods, numbers and un-derscores. Periods are used to give sense of struc-ture, and Haskell compilers will use them as indi-cations of the directory a particular source file is,but otherwise they have no meaning.The Haskell community has standardized a setof top-level module names such as Data, System,Network, etc. Be sure to consult them for an appro-priate place for your own module if you plan onreleasing it to the public.Imports The Haskell standard libraries are di-vided into a number of modules. The functionalityprovided by those libraries is accessed by import-ing into your source file. To import all everythingexported by a library, just use the module name:import Text.ReadEverything means everything: functions, data typesand constructors, class declarations, and even othermodules imported and then exported by the thatmodule. Importing selectively is accomplished bygiving a list of names to import. For example, herewe import some functions from Text.Read:import Text.Read (readParen, lex)Data types can imported in a number of ways. Wecan just import the type and no constructors:import Text.Read (Lexeme)c 2009 Justin Bailey. 6 jgbailey@codeslower.commailto:jgbailey@codeslower.comOf course, this prevents our module from pattern-matching on the values of type Lexeme. We canimport one or more constructors explicitly:import Text.Read (Lexeme(Ident, Symbol))All constructors for a given type can also be im-ported:import Text.Read (Lexeme(..))We can also import types and classes defined in themodule:import Text.Read (Read, ReadS)In the case of classes, we can import the functionsdefined for a class using syntax similar to import-ing constructors for data types:import Text.Read (Read(readsPrec, readList))Note that, unlike data types, all class functions areimported unless explicitly excluded. To only importthe class, we use this syntax:import Text.Read (Read())Exclusions If most, but not all, names are to beimported from a module, it would be tedious tolist them all. For that reason, imports can also bespecified via the hiding keyword:import Data.Char hiding (isControl, isMark)Except for instance declarations, any type, function,constructor or class can be hidden.Instance Declarations It must be noted thatinstance declarations cannot be excluded from im-port: all instance declarations in a module will beimported when the module is imported.Qualified Imports The names exported by amodule (i.e., functions, types, operators, etc.) canhave a prefix attached through qualified imports.This is particularly useful for modules which havea large number of functions having the same nameas Prelude functions. Data.Set is a good example:import qualified Data.Set as SetThis form requires any function, type, constructoror other name exported by Data.Set to now be pre-fixed with the alias (i.e., Set) given. Here is one wayto remove all duplicates from a list:removeDups a =Set.toList (Set.fromList a)A second form does not create an alias. Instead,the prefix becomes the module name. We can writea simple function to check if a string is all uppercase:import qualified CharallUpper str =all Char.isUpper strExcept for the prefix specified, qualified importssupport the same syntax as normal imports. Thename imported can be limited in the same ways asdescribed above.Exports If an export list is not provided, then allfunctions, types, constructors, etc. will be availableto anyone importing the module. Note that any im-ported modules are not exported in this case. Limit-ing the names exported is accomplished by addinga parenthesized list of names before the where key-word:module MyModule (MyType, MyClass, myFunc1...)whereThe same syntax as used for importing can be usedhere to specify which functions, types, construc-tors, and classes are exported, with a few differ-ences. If a module imports another module, it canalso export that module:module MyBigModule (module Data.Set, module Data.Char)whereimport Data.Setimport Data.CharA module can even re-export itself, which can beuseful when all local definitions and a given im-ported module are to be exported. Below we exportourselves and Data.Set, but not Data.Char:module AnotherBigModule (module Data.Set, module AnotherBigModule)whereimport Data.Setimport Data.Charc 2009 Justin Bailey. 7 jgbailey@codeslower.commailto:jgbailey@codeslower.comNewtypeWhile data introduces new values and type justcreates synonyms, newtype falls somewhere be-tween. The syntax for newtype is quite restrictedonly one constructor can be defined, and that con-structor can only take one argument. Continuingthe above example, we can define a Phone type asfollows:newtype Home = H Stringnewtype Work = W Stringdata Phone = Phone Home WorkAs opposed to type, the H and W values onPhone are not just String values. The typecheckertreats them as entirely new types. That means ourlowerName function from above would not compile.The following produces a type error:lPhone (Phone hm wk) =Phone (lower hm) (lower wk)Instead, we must use pattern-matching to get to thevalues to which we apply lower:lPhone (Phone (H hm) (W wk)) =Phone (H (lower hm)) (W (lower wk))The key observation is that this keyword does notintroduce a new value; instead it introduces a newtype. This gives us two very useful properties: No runtime cost is associated with the newtype, since it does not actually produce newvalues. In other words, newtypes are abso-lutely free! The type-checker is able to enforce that com-mon types such as Int or String are used inrestricted ways, specified by the programmer.Finally, it should be noted that any derivingclause which can be attached to a data declarationcan also be used when declaring a newtype.ReturnSee do on page 4.TypeThis keyword defines a type synonym (i.e., alias).This keyword does not define a new type, like dataor newtype. It is useful for documenting code butotherwise has no effect on the actual type of a givenfunction or value. For example, a Person data typecould be defined as:data Person = Person String Stringwhere the first constructor argument representstheir first name and the second their last. How-ever, the order and meaning of the two argumentsis not very clear. A type declaration can help:type FirstName = Stringtype LastName = Stringdata Person = Person FirstName LastNameBecause type introduces a synonym, type checkingis not affected in any way. The function lower, de-fined as:lower s = map toLower swhich has the typelower :: String -> Stringcan be used on values with the type FirstName orLastName just as easily:lName (Person f l ) =Person (lower f) (lower l)Because type is just a synonym, it cannot declaremultiple constructors the way data can. Type vari-ables can be used, but there cannot be more thanthe type variables declared with the original type.That means a synonym like the following is possi-ble:type NotSure a = Maybe abut this not:type NotSure a b = Maybe aNote that fewer type variables can be used, whichuseful in certain instances.WhereSimilar to let, where defines local functions andconstants. The scope of a where definition is thecurrent function. If a function is broken into multi-ple definitions through pattern-matching, then thescope of a particular where clause only applies tothat definition. For example, the function resultbelow has a different meaning depending on thearguments given to the function strlen:strlen [] = resultwhere result = "No string given!"strlen f = result ++ " characters long!"where result = show (length f)Where vs. Let A where clause can only be de-fined at the level of a function definition. Usually,that is identical to the scope of let definition. Theonly difference is when guards are being used. Thec 2009 Justin Bailey. 8 jgbailey@codeslower.commailto:jgbailey@codeslower.comscope of the where clause extends over all guards.In contrast, the scope of a let expression is onlythe current function clause and guard, if any.Declarations, Etc.The following section details rules on function dec-larations, list comprehensions, and other areas ofthe language.Function DefinitionFunctions are defined by declaring their name, anyarguments, and an equals sign:square x = x * xAll functions names must start with a lowercase let-ter or _. It is a syntax error otherwise.Pattern Matching Multiple clauses of a func-tion can be defined by pattern-matching on thevalues of arguments. Here, the the agree functionhas four separate cases:-- Matches when the string "y" is given.agree1 "y" = "Great!"-- Matches when the string "n" is given.agree1 "n" = "Too bad."-- Matches when string beginning-- with 'y' given.agree1 ('y':_) = "YAHOO!"-- Matches for any other value given.agree1 _ = "SO SAD."Note that the _ character is a wildcard andmatches any value.Pattern matching can extend to nested values.Assuming this data declaration:data Bar = Bil (Maybe Int) | Bazand recalling the definition of Maybe from page 2we can match on nested Maybe values when Bil ispresent:f (Bil (Just _)) = ...f (Bil Nothing) = ...f Baz = ...Pattern-matching also allows values to be assignedto variables. For example, this function determinesif the string given is empty or not. If not, the valuebound to str is converted to lower case:toLowerStr [] = []toLowerStr str = map toLower strNote that str above is similer to _ in that it willmatch anything; the only difference is that thevalue matched is also given a name.n + k Patterns This (sometimes controversial)pattern-matching facility makes it easy to matchcertain kinds of numeric expressions. The ideais to define a base case (the n portion) with aconstant number for matching, and then to defineother matches (the k portion) as additives to thebase case. Here is a rather inefficient way of testingif a number is even or not:isEven 0 = TrueisEven 1 = FalseisEven (n + 2) = isEven nArgument Capture Argument capture is usefulfor pattern-matching a value and using it, withoutdeclaring an extra variable. Use an @ symbol inbetween the pattern to match and the variable tobind the value to. This facility is used below tobind the head of the list in l for display, while alsobinding the entire list to ls in order to compute itslength:len ls@(l:_) = "List starts with " ++show l ++ " and is " ++show (length ls) ++ " items long."len [] = "List is empty!"Guards Boolean functions can be used asguards in function definitions along with patternmatching. An example without pattern matching:which n| n == 0 = "zero!"| even n = "even!"| otherwise = "odd!"Notice otherwise it always evaluates to true andcan be used to specify a default branch.Guards can be used with patterns. Here is afunction that determines if the first character in astring is upper or lower case:what [] = "empty string!"what (c:_)| isUpper c = "upper case!"| isLower c = "lower case"| otherwise = "not a letter!"Matching & Guard Order Pattern-matchingproceeds in top to bottom order. Similarly, guardexpressions are tested from top to bottom. For ex-ample, neither of these functions would be very in-teresting:allEmpty _ = FalseallEmpty [] = Truec 2009 Justin Bailey. 9 jgbailey@codeslower.commailto:jgbailey@codeslower.comalwaysEven n| otherwise = False| n `div` 2 == 0 = TrueRecord Syntax Normally pattern matching oc-curs based on the position of arguments in thevalue being matched. Types declared with recordsyntax, however, can match based on those recordnames. Given this data type:data Color = C { red, green, blue :: Int }we can match on green only:isGreenZero (C { green = 0 }) = TrueisGreenZero _ = FalseArgument capture is possible with this syntax, al-though it gets clunky. Continuing the above, wenow define a Pixel type and a function to replacevalues with non-zero green components with allblack:data Pixel = P Color-- Color value untouched if green is 0setGreen (P col@(C { green = 0 })) = P colsetGreen _ = P (C 0 0 0)Lazy Patterns This syntax, also known as ir-refutable patterns, allows pattern matches which al-ways succeed. That means any clause using thepattern will succeed, but if it tries to actually usethe matched value an error may occur. This is gen-erally useful when an action should be taken onthe type of a particular value, even if the value isntpresent.For example, define a class for default values:class Def a wheredefValue :: a -> aThe idea is you give defValue a value of the righttype and it gives you back a default value for thattype. Defining instances for basic types is easy:instance Def Bool wheredefValue _ = Falseinstance Def Char wheredefValue _ = ' 'Maybe is a littler trickier, because we want to geta default value for the type, but the constructormight be Nothing. The following definition wouldwork, but its not optimal since we get Nothingwhen Nothing is passed in.instance Def a => Def (Maybe a) wheredefValue (Just x) = Just (defValue x)defValue Nothing = NothingWed rather get a Just (default value) back instead.Here is where a lazy pattern saves us we can pre-tend that weve matched Just x and use that to geta default value, even if Nothing is given:instance Def a => Def (Maybe a) wheredefValue ~(Just x) = Just (defValue x)As long as the value x is not actually evaluated,were safe. None of the base types need to look at x(see the _ matches they use), so things will workjust fine.One wrinkle with the above is that we mustprovide type annotations in the interpreter or thecode when using a Nothing constructor. Nothinghas type Maybe a but, if not enough other informa-tion is available, Haskell must be told what a is.Some example default values:-- Return "Just False"defMB = defValue (Nothing :: Maybe Bool)-- Return "Just ' '"defMC = defValue (Nothing :: Maybe Char)List ComprehensionsA list comprehension consists of four types of ele-ments: generators, guards, local bindings, and targets.A list comprehension creates a list of target valuesbased on the generators and guards given. Thiscomprehension generates all squares:squares = [x * x | x , let z = min a b, z < c ]Comprehensions are not limited to numbers. Anylist will do. All upper case letters can be generated:ups =[c | c String -> StringAllowable symbols which can be used to define op-erators are:# $ % & * + . / < = > ? @ \ ^ | - ~However, there are several operators which can-not be redefined. They are: and =. The last,=, cannot be redefined by itself, but can be used aspart of multi-character operator. The bind func-tion, >>=, is one example.Precedence & Associativity The precedenceand associativity, collectively called fixity, of anyoperator can be set through the infix, infixr andinfixl keywords. These can be applied both totop-level functions and to local definitions. Thesyntax is:infix | infixr | infixl precedence opwhere precedence varies from 0 to 9. Op can actu-ally be any function which takes two arguments(i.e., any binary operation). Whether the operatoris left or right associative is specified by infixl orinfixr, respectively. Such infix declarations haveno associativity.Precedence and associativity make many of therules of arithmetic work as expected. For ex-ample, consider these minor updates to the prece-dence of addition and multiplication:infixl 8 `plus1`plus1 a b = a + binfixl 7 `mult1`mult1 a b = a * bThe results are surprising:> 2 + 3 * 517> 2 `plus1` 3 `mult1` 525Reversing associativity also has interesting effects.Redefining division as right associative:infixr 7 `div1`div1 a b = a / bWe get interesting results:> 20 / 2 / 25.0> 20 `div1` 2 `div1` 220.0CurryingIn Haskell, functions do not have to get all oftheir arguments at once. For example, consider theconvertOnly function, which only converts certainelements of string depending on a test:convertOnly test change str =map (\c -> if test cthen change celse c) strc 2009 Justin Bailey. 11 jgbailey@codeslower.commailto:jgbailey@codeslower.comUsing convertOnly, we can write the l33t functionwhich converts certain letters to numbers:l33t = convertOnly isL33t toL33twhereisL33t 'o' = TrueisL33t 'a' = True-- etc.isL33t _ = FalsetoL33t 'o' = '0'toL33t 'a' = '4'-- etc.toL33t c = cNotice that l33t has no arguments specified. Also,the final argument to convertOnly is not given.However, the type signature of l33t tells the wholestory:l33t :: String -> StringThat is, l33t takes a string and produces a string.It is a constant, in the sense that l33t always re-turns a value that is a function which takes a stringand produces a string. l33t returns a curriedform of convertOnly, where only two of its threearguments have been supplied.This can be taken further. Say we want to writea function which only changes upper case letters.We know the test to apply, isUpper, but we dontwant to specify the conversion. That function canbe written as:convertUpper = convertOnly isUpperwhich has the type signature:convertUpper :: (Char -> Char)-> String -> StringThat is, convertUpper can take two arguments. Thefirst is the conversion function which converts indi-vidual characters and the second is the string to beconverted.A curried form of any function which takesmultiple arguments can be created. One way tothink of this is that each arrow in the functionssignature represents a new function which can becreated by supplying one more argument.Sections Operators are functions, and they canbe curried like any other. For example, a curriedversion of + can be written as:add10 = (+) 10However, this can be unwieldy and hard to read.Sections are curried operators, using parenthe-ses. Here is add10 using sections:add10 = (10 +)The supplied argument can be on the right or left,which indicates what position it should take. Thisis important for operations such as concatenation:onLeft str = (++ str)onRight str = (str ++)Which produces quite different results:> onLeft "foo" "bar""barfoo"> onRight "foo" "bar""foobar"Updating values and record syntaxHaskell is a pure language and, as such, has nomutable state. That is, once a value is set it neverchanges. Updating is really a copy operation,with new values in the fields that changed. Forexample, using the Color type defined earlier, wecan write a function that sets the green field to zeroeasily:noGreen1 (C r _ b) = C r 0 bThe above is a bit verbose and can be rewriten us-ing record syntax. This kind of update only setsvalues for the field(s) specified and copies the rest:noGreen2 c = c { green = 0 }Here we capture the Color value in c and return anew Color value. That value happens to have thesame value for red and blue as c and its greencomponent is 0. We can combine this with patternmatching to set the green and blue fields to equalthe red field:makeGrey c@(C { red = r }) =c { green = r, blue = r }Notice we must use argument capture (c@) to getthe Color value and pattern matching with recordsyntax (C { red = r}) to get the inner red field.Anonymous FunctionsAn anonymous function (i.e., a lambda expressionor lambda for short), is a function without a name.They can be defined at any time like so:\c -> (c, c)which defines a function which takes an argumentand returns a tuple containing that argument inboth positions. They are useful for simple func-tions which dont need a name. The following de-termines if a string has mixed case (or is all whites-pace):c 2009 Justin Bailey. 12 jgbailey@codeslower.commailto:jgbailey@codeslower.commixedCase str =all (\c -> isSpace c ||isLower c ||isUpper c) strOf course, lambdas can be the returned from func-tions too. This classic returns a function which willthen multiply its argument by the one originallygiven:multBy n = \m -> n * mFor example:> let mult10 = multBy 10> mult10 10100Type SignaturesHaskell supports full type inference, meaning inmost cases no types have to be written down. Typesignatures are still useful for at least two reasons.DocumentationEven if the compiler can figureout the types of your functions, other pro-grammers or even yourself might not be ableto later. Writing the type signatures on alltop-level functions is considered very goodform.SpecializationTypeclasses allow functions withoverloading. For example, a function tonegate any list of numbers has the signature:negateAll :: Num a => [a] -> [a]However, for efficiency or other reasons youmay only want to allow Int types. You wouldaccomplish that with a type signature:negateAll :: [Int] -> [Int]Type signatures can appear on top-level func-tions and nested let or where definitions. Gener-ally this is useful for documentation, although insome cases they are needed to prevent polymor-phism. A type signature is first the name of theitem which will be typed, followed by a ::, fol-lowed by the types. An example of this has alreadybeen seen above.Type signatures do not need to appear directlyabove their implementation. They can be specifiedanywhere in the containing module (yes, even be-low!). Multiple items with the same signature canalso be defined together:pos, neg :: Int -> Int...pos x | x < 0 = negate x| otherwise = xneg y | y > 0 = negate y| otherwise = yType Annotations Sometimes Haskell cannotdetermine what type is meant. The classic demon-stration of this is the so-called show . read prob-lem:canParseInt x = show (read x)Haskell cannot compile that function because itdoes not know the type of x. We must limit thetype through an annotation:canParseInt x = show ((read x) :: Int)Annotations have the same syntax as type signa-tures, but may adorn any expression.Unit() unit type and unit value. The value andtype that represents no useful information.ContributorsMy thanks to those who contributed patches anduseful suggestions: Dave Bayer, Cale Gibbard,Stephen Hicks, Kurt Hutchinson, Johan Kiviniemi,Adrian Neumann, Barak Pearlmutter, Lanny Rip-ple, Markus Roberts, Holger Siegel, Leif Warner,and Jeff Zaroyko.VersionThis is version 1.8. The source can be foundat GitHub2. The latest released version of thePDF can be downloaded from Hackage3. VisitCodeSlower.com4 for other projects and writings.2http://github.com/m4dc4p/cheatsheet3http://hackage.haskell.org/cgi-bin/hackage-scripts/package/CheatSheet4http://blog.codeslower.com/c 2009 Justin Bailey. 13 jgbailey@codeslower.comhttp://github.com/m4dc4p/cheatsheethttp://hackage.haskell.org/cgi-bin/hackage-scripts/package/CheatSheethttp://blog.codeslower.com/mailto:jgbailey@codeslower.com

Recommended

View more >