What's going on in Haskell?
Definition of receipt:
"In Haskell, a derived instance is an instance declaration that is generated automatically in conjunction with a data or new type declaration. Derived the body of the derived instance declaration syntactically from the definition of the associated type."
I really don't get it, to be honest.
The code below is taken from: Link
data BaseballPlayer = Pitcher
| Catcher
| Infielder
| Outfielder
deriving Show
barryBonds :: BaseballPlayer -> Bool
barryBonds Outfielder = True
barryInOf = print(barryBonds Outfielder)
My question is what does the inference statement do in this particular case, and what does the inference-expression do in general?
This question is not a duplicate: how does haskell work in OBS: I am not asking how it works in terms of source code, I am asking what it does.
source to share
In this particular case, it generates an instance Show
for your type as shown below:
instance Show BaseballPlayer where
show Pitcher = "Pitcher"
show Catcher = "Catcher"
show Infielder = "Infielder"
show Outfielder = "Outfielder"
This way values of the type BaseballPlayer
can be converted to string, for example. on print
.
The string is chosen so that it is a valid Haskell expression that can be restored to its original value upon evaluation.
The general case is a little more complicated, but follows the same idea: converting a value to a string of a Haskell expression. For example,
data T = T Int (Maybe Bool) deriving Show
will make the instance so that
show (T 1 Nothing) = "T 1 Nothing"
show (T 2 (Just 3)) = "T 2 (Just 3)"
Note how the parentheses are also generated in the latter case. This is done using a member of the class showsPrec
, but it is not that important.
source to share
In short:
deriving
automatically implements functions for several types of Haskell classes such as Show
and Eq
. It can't be done with arbitrary types, but the ones it deriving
works for are simple enough to automatically implement.
The Show
typeclass defines functions to represent data types as String
.
In details:
Are you familiar with classes?
https://www.haskell.org/tutorial/classes.html
Typeclasses are similar to interfaces in Java: they define several functions that any data type that wants to use those functions can implement can implement.
For example, let's say we have a class like this:
class Comparable a where
lessThan :: a -> a -> Bool
equalsTo :: a -> a -> Bool
Beware of the word class
. This means that the Haskell customization is using the text type, not the typical "class" you would hear in object-oriented languages. a
here is a type of filler similar to how you would expect templates to work in C ++ and generics to behave in Java.
Let's say we define a data type like this:
data Color = Red | Green | Blue
To make it Comparable
work with Color
, we will implement instance
Comparable
:
instance Comparable Color where
lessThan Red Green = True
lessThan Red Blue = True
lessThan Green Blue = True
lessThan _ _ = False
equalsTo Red Red = True
equalsTo Green Green = True
equalsTo Blue Blue = True
equalsTo _ _ = False
Roughly speaking, this now allows you to "compare" Red
, Green
and Blue
with each other. But was there some way that GHC could automatically assume that this was exactly the "order" you wanted?
Taking a step back, the typeclass Show
has a similar structure:
https://hackage.haskell.org/package/base-4.9.1.0/docs/src/GHC.Show.html#Show
class Show a where
showsPrec :: Int -> a -> ShowS
show :: a -> String
showList :: [a] -> ShowS
showsPrec _ x s = show x ++ s
show x = shows x ""
showList ls s = showList__ shows ls s
Something to note is that functions within a typeclass can be defined with each other. Indeed, we could just as easily do:
class Comparable a where
lessThan :: a -> a -> Bool
equalsTo :: a -> a -> Bool
greaterThan :: a -> a -> Bool
greaterThan lhs rhs = not (lessThan lhs rhs && equalsTo lhs rhs)
However, the key point is this: For arbitrary user-defined class types, GHC has no idea how their functionality should be implemented when you try to bind a type class to a data type such as Color
or BaseballPlayer
. For some types, such as Show
, Eq
, Ord
, etc., where the functionality is quite simple, the GHC can generate standard implementation, which of course you can overwrite your own.
Indeed, experiment by trying to compile the following:
data Color = Red | Green | Blue deriving (Comparable)
As a result, I get the following:
test.hs:9:43:
Can't make a derived instance of ‘Comparable Color’:
‘Comparable’ is not a derivable class
Try enabling DeriveAnyClass
In the data declaration for ‘Color’
Certainly some GHC extensions can be used to increase the power deriving
, but for another day :)
source to share
Inference means that your datatype can automatically "infer" instances for certain class classes. In this case it BaseballPlayer
outputs Show
which means that we can use any function that requires an instance Show
to work with BaseballPlayer
.
Automatic retrieval makes it easier for you to work with the template. The most common types of classes to get automatically are Show
and Eq
, because the compiler can make very reasonable values for these types.
source to share