Creating Classes
This material motivated by Haskell Programming from first principles, by Allen and Moronuki. The Github user kingparra has notes that might be useful.
If you are using a compiler older than GHC 2021, you will need to add
FlexibleInstances to your language. You need this line if you have
problems with your Demo String
(see below).
{-# LANGUAGE FlexibleInstances #-}
Newtype
You already know two ways to create a type:
type MyInt1 = Int
data MyInt2 = MyInt2 Int
There is a way that is similar to the second that is commonly used to rename a single type.
newtype MyInt3 = MyInt3 Int
A newtype renames a single type. It cannot hold more than one thing. It is used to be able to tell the difference between things that would otherwise look the same.
For example, if you wanted to use Double
to represent both money and
time, you could use newtypes to distinguish them.
newtype Money = Money Double
newtype Time = Time Double
This makes mistaking dollars for hours impossible inside your program.
Usually you want to use deriving (Show, Eq)
just like in data
.
Creating Classes I
A class declaration specifies the names and signatures of methods that have to be available for instances of that class.
The Demo
class below just requires a function that turns takes in
something and produces an Int.
class Demo a where
numit :: a -> Int
In order to make String
an instance of the Demo
class, we need a
way of getting a number out of any String. We could use the length
.
instance Demo String where
numit s = length s
Technically, less logical choices would also be allowed like always making the output number zero.
-
Make a
Posn
into an instance of theDemo
class by adding the coordinates.type Posn = (Int, Int)
-
Make a
SInt
into an instance ofDemo
my multiplying the number by the length of the String.data SInt = SInt Int String
Creating Classes II
-
Write a class declaration for
StepTwo
so thatStepTwo a
means that there is a methodrev :: a -> a
. -
Make
Integer
an instance ofStepTwo
where rev makes a number into its opposite. -
Make
String
into an instance ofStepTwo
by havingrev
reverse the incoming string. -
Write a class declaration for
ProbThree
so thatProbThree a
means there is a combination method,comb :: a -> a -> a
-
Make
Double
an instance ofProbThree
by using multiplication to combine. -
Write a class declaration for
Bracket
that abstracts the patternaITEMa
, like"apple"
becomes"DocappleDoc"
. Thebasex
method (below) would give"Doc"
in this case.Write code so that
Bracket b
means that there are two items available:basex :: b brack :: b -> String -> b
-
(Danger, do at the end after all others.) Make
String
an instance ofBracket
so that these tests pass:x :: String x = basex y = brack x "-WY-" checkBracket = [x == "MrH", y == "MrH-WY-MrH" ]
-
(Extreme Danger, do very last.) Make a
newtype IntBrack
that is just like anInt
. MakeIntBrack
an instance of theBracket
class by choosing the number 42 as thebasex
and doing42 + length word + 42
for thebrack
operation. Pay attention to types required by the signatures!Making the 42 in
brack
match value inbasex
is a little extra challenge.
Creating Classes III
-
Create a
newtype
calledAInt
that is the same as anInt
. Automatically addAInt
to theEq
typeclass. -
Add
AInt
to theShow
typeclass so thatshow (AInt 7)
returns"I:7"
.main = do let x = AInt 7 putStrLn $ show x
-
Write a function
g :: Int -> AInt
that doubles the number given. -
Verify that
g 5 == AInt 10
butg (g 5)
is an error. -
Add
AInt
to theOrd
typeclass by providing a(<=)
method that compares the ints. -
Create a
Scholar
class so thatScholar a
means there is methodstudy :: a -> Int
. -
Make
AInt
an instance of theScholar
class, where the result of studying is just 5 more than the integer in theAInt
. -
Make
String
an instance of theScholar
class, where the result of studying is 5 times the length of the string.
Creating Classes IV
You can have functions that take in more than one item in your class.
-
Make a
Join
class that has a methodtogether :: a -> a -> a
. -
Make
[Int]
an instance of theJoin
class in some logical way. -
Make
Int
an instance of theJoin
class by multiplying. -
Make
AInt
an instance of theJoin
class by adding.
Creating Classes V
In this section we declare and use the class Bizarre a
, which has
two methods
weird :: a
(<-->) :: Int -> a -> [a]
-
Declare
Bizarre
. -
Make
Int
an instance of this class. -
Make
String
an instance of this class. -
A multipart question, in which you make a type called
Boring
and add it to theBizarre
typeclass.- Make your own newtype
Boring
be an Int. DeriveShow
so you can see it. - Write the signatures of the functions that you need to write.
- Now write them, by always
choosing 1 and the list [2,3]. (Literally writing
1
is incorrect because it is the wrong type. Check your type signatures before you try to write a solution.)
Testing your code:
testBizarre = do print (weird :: Int) print $ (<-->) 5 (8::Int) print (weird :: String) print $ (<-->) 3 "Good" print (weird :: Boring) print $ (<-->) 5 (Boring 7)
- Make your own newtype