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 compile before 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"
. Themyname
method (below) would give"Doc"
in this case.Write code so that
Bracket b
means that there are two items available:myname :: b brack :: String -> b
-
Make
String
an instance ofBracket
so that these tests pass:x :: String x = myname y = brack "WY" checkBracket = [x == "MrHarris", y == "MrHarrisWYMrHarris" ]
-
Make a
newtype IntBrack
that is just like anInt
. MakeIntBrack
an instance of theBracket
class by choosing the number 42 as the “name” and doing42 + length word + 42
for thebrack
operation.
Creating Classes III
-
Create a new type called
AInt
that is the same as anInt
. -
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 theShow
typeclass so thatshow (AInt 7)
returns"I:7"
. (Optional: verify this on the computer.)main = do let x = AInt 7 putStrLn $ show x
-
(Bonus) 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 the AInt. -
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. - Make your own newtype Boring (Int) and make it Bizarre by always choosing 1 and the list [2,3]. (Not quite literally…)
Defining Typeclasses
Make a new typeclass building on something that already exists. For
example, a type called Maxer
that acts the same as Integer
.
newtype Maxer = Maxer Integer
New define a typeclass called Sniffle
.
class Sniffle a where
sneeze :: a -> a
cough :: a
Then an instance of the typeclass:
instance Sniffle Maxer where
cough = Maxer 0
sneeze (Maxer x) (Maxer y) = Maxer (x + y)
–>