This declares a type Slot1 with two constructors,Slot1 and Empty1. The Slot1 constructor can takean argument of any type, which is reprented by thetype 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 argument.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 cannothave the same name as another function inscope. Thus the “ct” prefix on each above. Multipleconstructors (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 captureand “updating.”Class Constraints Data types can be declaredwith class constraints on the type variables, butthis practice is generally discouraged. It is generallybetter to hide the “raw” data constructors usingthe module system and instead export “smart”constructors 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 tediuos to define yet very necessary, suchas the ability to convert to and from strings, comparefor equality, or order in a sequence. Thesecapabilities are defined as typeclasses in Haskell.Because seven of these operations are so common,Haskell provides the deriving keywordwhich will automatically implement the typeclasson the associated type. The seven supported typeclassesare: Eq, Read, Show, Ord, Enum, Ix, andBounded.Two forms of deriving are possible. The first isused when a type only derives on class:data Priority = Low | Medium | Highderiving ShowThe second is used when multiple classes are derived: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 keywordabove.DoThe do keyword indicates that the code to followwill be in a monadic context. Statements are separatedby newlines, assignment is indicated by 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 ...c○ 2008 Justin Bailey. 4 jgbailey@codeslower.com
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 action:right1 fileName = doexists