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
Posninto an instance of theDemoclass by adding the coordinates.type Posn = (Int, Int) -
Make a
SIntinto an instance ofDemomy multiplying the number by the length of the String.data SInt = SInt Int String
Creating Classes II
-
Write a class declaration for
StepTwoso thatStepTwo ameans that there is a methodrev :: a -> a. -
Make
Integeran instance ofStepTwowhere rev makes a number into its opposite. -
Make
Stringinto an instance ofStepTwoby havingrevreverse the incoming string. -
Write a class declaration for
ProbThreeso thatProbThree ameans there is a combination method,comb :: a -> a -> a -
Make
Doublean instance ofProbThreeby using multiplication to combine. -
Write a class declaration for
Bracketthat abstracts the patternaITEMa, like"apple"becomes"DocappleDoc". Thebasexmethod (below) would give"Doc"in this case.Write code so that
Bracket bmeans that there are two items available:basex :: b brack :: b -> String -> b -
(Danger, do at the end after all others.) Make
Stringan instance ofBracketso that these tests pass:x :: String x = basex y :: String y = brack x "-WY-" checkBracket = [x == "MrH", y == "MrH-WY-MrH" ] -
(Extreme Danger, do very last.) Make a
newtype IntBrackthat is just like anInt. MakeIntBrackan instance of theBracketclass by choosing the number 42 as thebasexand doing42 + length word + 42for thebrackoperation. Pay attention to types required by the signatures!Making the 42 in
brackmatch value inbasexis a little extra challenge.
Creating Classes III
-
Create a
newtypecalledAIntthat is the same as anInt. Automatically addAIntto theEqtypeclass. -
Add
AIntto theShowtypeclass so thatshow (AInt 7)returns"I:7".main = do let x = AInt 7 putStrLn $ show x -
Write a function
g :: Int -> AIntthat doubles the number given. -
Verify that
g 5 == AInt 10butg (g 5)is an error. -
Add
AIntto theOrdtypeclass by providing a(<=)method that compares the ints. -
Create a
Scholarclass so thatScholar ameans there is methodstudy :: a -> Int. -
Make
AIntan instance of theScholarclass, where the result of studying is just 5 more than the integer in theAInt. -
Make
Stringan instance of theScholarclass, 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
Joinclass that has a methodtogether :: a -> a -> a. -
Make
[Int]an instance of theJoinclass in some logical way. -
Make
Intan instance of theJoinclass by multiplying. -
Make
AIntan instance of theJoinclass 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
Intan instance of this class. -
Make
Stringan instance of this class. -
A multipart question, in which you make a type called
Boringand add it to theBizarretypeclass.- Make your own newtype
Boringbe an Int. DeriveShowso 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
1is 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