9. Algebraic data-types (ADT)
A simple definition of an ADT in Haskell is:
data Shape = Rectangle Integer Integer | Circle Integer
The part data Shape = …
of a definition introduces a new type called Shape
in the program. Objects of any type (including Shape
) are constructed functionally by calling special functions called data constructors. Our definition has two data constructors:
Rectangle
Circle
Data constructors are defined as part of the ADT definition (the right-hand size of =
). They are functions which take different other values (or even no value) and always return a value of the defined ADT. In our example, the definition:
Rectangle Integer Integer
creates the data constructorRectangle :: Integer → Integer → Shape
Circle Integer
creates the data constructorCircle :: Integer → Shape
We can define functions over newly-defined ADTs by using pattern matching. The permited patterns for an ADT are its data constructors. Example:
area :: Shape -> Integer area (Rectangle x y) = x * y area (Circle x) = 3*x*x
9.1. A few simple ADTs
9.1.1. Create a datatype which may encode 3 different values: true, false and unknown. Implement the operation and over such values. (when one of and's operands is unknown, it's value is unknown).
9.1.2. Create a datatype Nat
which encodes natural numbers, following the rules:
- zero is a special natural number
- each non-zero natural number is the successor of some natural number
Implement the encoding conversion functions:
toInteger :: Nat -> Integer fromInteger :: Integer -> Nat -- the integer will always be positive
9.1.3. Implement the ADT of an ID, where an ID may be defined as:
- a CNP (Personal Identification Number). In Romania, the CNP has the following structure:
SDDMMYYXXXXXX
where:S
is the gender and birth-period digit:S=1
- male born before year2000
S=2
- female born before year2000
S=5
- male born starting from2000
S=6
- female born starting from2000
DDMMYY
indicates the birth date (D-day, M-month, Y-year)XXXXXX
- the last six digits encode other info which are not relevant for us.
or
- a collection of strings which represent:
- Full name
- Complete year of birth
- gender (
male
orfemale
)
- Implement the type
Gender
(it is better to define a gender separately, instead of using True/False or strings, because it reduces the occurrence of bugs). - Implement a function which takes a gender, a list of ID's and returns those having that gender (you can safely assume that the CNP has valid form. Hint: use functional closures and higher-order functions).
getGender :: Gender -> [ID] -> [ID]
9.1.4. Implement a function which takes a year, a list of ID's and returns those ID's which were born in that year. (Hint, write an auxiliary function which extracts the birth-year of an id. Combine it with higher-order functions).
getYear :: Integer -> [ID] -> [ID]
9.1.5. Define an ADTs called Range
, which encodes a range of birth years together with a gender. For instance:
- one range could designate people of gender female born between 1956 and 2005
- another range could designate both male and female born between 1991 and 2006
Write a function which takes a Range
, a list of ID's and returns those IDs within the range.
getRange :: Range -> [ID] -> [ID]
9.2. Polymorphic datatypes
Polymorphic datatypes are those which may be parametrised, they may be constructed with respect to other types. A polymorphic datatype is a definition of the form:
data PolyType a b c d ... =
where a, b, c, d, …
are type variables. Type variables can designate any other possible type (although it is possible to add constraints), and they will appear in the data constructor definitions.
The simplest polymorphic type definition is the polymorphic list:
data List a = Null | Cons a (List a)
9.2.1. Implement function size :: List a → Integer
.
9.2.2. Implement reverse: reverse :: List a → List a
. (Hint: which auxiliary operations are necessary?).
9.2.3. Implement the conversion functions toHaskellList :: List a → [a]
and fromHaskellList :: [a] → List a
.