
I am trying to understand the Typeclassopedia article by Brent Yorgey in Monad.Reader. I am simultaneously working my way through Real World Haskell, so I may be doing things a bit out of order. Here is my question (which may itself be unclear, as I am a true *noob* at this): Why are there two different kinds of "constraints" in a signature, viz. those that specify types and those that specify typeclasses? For instance, we could write this: f :: (Num d) => d -> String -> Int In my example above there are two kinds of things that constrain f: (a) d must be an instance of Num; and (b) the second argument to f must be of type String, and f must return an Int. I (more or less) understand that types and typeclasses are not the same. But it seems to me that they do conflate in a way, if we think of types as defined by functions on them. So why should we not be able to write: f :: Num d -> String -> Int TIA. Best, - Phil -

On Fri, May 06, 2011 at 11:34:47AM -0400, Philippe Sismondi wrote:
I am trying to understand the Typeclassopedia article by Brent Yorgey in Monad.Reader. I am simultaneously working my way through Real World Haskell, so I may be doing things a bit out of order.
Here is my question (which may itself be unclear, as I am a true *noob* at this):
Why are there two different kinds of "constraints" in a signature, viz. those that specify types and those that specify typeclasses? For instance, we could write this:
f :: (Num d) => d -> String -> Int
In my example above there are two kinds of things that constrain f:
(a) d must be an instance of Num; and (b) the second argument to f must be of type String, and f must return an Int.
I (more or less) understand that types and typeclasses are not the same. But it seems to me that they do conflate in a way, if we think of types as defined by functions on them. So why should we not be able to write:
f :: Num d -> String -> Int
Your intuition is good: in fact, this is the way type classes are implemented under the hood! A constraint (Num d => ...) is translated into a parameter (Num d -> ...) where an argument of type 'Num d' is a *dictionary* (i.e. record) containing implementations of the Num methods for the type d. The reason for the special syntax is that for you, the programmer, there is a fundamental difference: arguments (dictionaries) coming before => are *implicit*, that is, you do not provide them yourself, the compiler figures out what they should be and supplies them automatically. Arguments coming after => are explicit arguments which you must provide when calling a function. One might ask why it is not possible to explicitly provide dictionaries if you want: this is a good question and an interesting area of research, and very related to the notion of "functors" (i.e. parameterized modules, not the same thing as Haskell Functors) in ML. -Brent

On 2011-05-06, at 12:12 PM, Brent Yorgey wrote:
On Fri, May 06, 2011 at 11:34:47AM -0400, Philippe Sismondi wrote:
I am trying to understand the Typeclassopedia article by Brent Yorgey in Monad.Reader. I am simultaneously working my way through Real World Haskell, so I may be doing things a bit out of order.
Here is my question (which may itself be unclear, as I am a true *noob* at this):
Why are there two different kinds of "constraints" in a signature, viz. those that specify types and those that specify typeclasses? For instance, we could write this:
f :: (Num d) => d -> String -> Int
In my example above there are two kinds of things that constrain f:
(a) d must be an instance of Num; and (b) the second argument to f must be of type String, and f must return an Int.
I (more or less) understand that types and typeclasses are not the same. But it seems to me that they do conflate in a way, if we think of types as defined by functions on them. So why should we not be able to write:
f :: Num d -> String -> Int
Your intuition is good: in fact, this is the way type classes are implemented under the hood! A constraint (Num d => ...) is translated into a parameter (Num d -> ...) where an argument of type 'Num d' is a *dictionary* (i.e. record) containing implementations of the Num methods for the type d.
The reason for the special syntax is that for you, the programmer, there is a fundamental difference: arguments (dictionaries) coming before => are *implicit*, that is, you do not provide them yourself, the compiler figures out what they should be and supplies them automatically. Arguments coming after => are explicit arguments which you must provide when calling a function.
One might ask why it is not possible to explicitly provide dictionaries if you want: this is a good question and an interesting area of research, and very related to the notion of "functors" (i.e. parameterized modules, not the same thing as Haskell Functors) in ML.
-Brent
Thanks for the quick reply, Brent. It will take me a day or so to digest your answer. - Phil -

2011/5/8 Philippe Sismondi
On 2011-05-06, at 12:12 PM, Brent Yorgey wrote:
On Fri, May 06, 2011 at 11:34:47AM -0400, Philippe Sismondi wrote:
I am trying to understand the Typeclassopedia article by Brent Yorgey in Monad.Reader. I am simultaneously working my way through Real World Haskell, so I may be doing things a bit out of order.
I (more or less) understand that types and typeclasses are not the same. But it seems to me that they do conflate in a way, if we think of types as defined by functions on them. So why should we not be able to write:
f :: Num d -> String -> Int
You can have multiple class constraints, so it's not clear how you would write them using your syntax; how would you write : f :: (Eq d, Ord d) => d -> String -> Int ? Also, you only have to tell the class constraint once, with your syntax g :: (Num d) => d -> d -> d would become: g :: Num d -> Num d -> Num d but that syntax lets you write nonsense : h :: Num d -> Eq d -> Ord d David.

On 2011-05-09, at 4:04 AM, David Virebayre wrote:
2011/5/8 Philippe Sismondi
: On 2011-05-06, at 12:12 PM, Brent Yorgey wrote:
On Fri, May 06, 2011 at 11:34:47AM -0400, Philippe Sismondi wrote:
I am trying to understand the Typeclassopedia article by Brent Yorgey in Monad.Reader. I am simultaneously working my way through Real World Haskell, so I may be doing things a bit out of order.
I (more or less) understand that types and typeclasses are not the same. But it seems to me that they do conflate in a way, if we think of types as defined by functions on them. So why should we not be able to write:
f :: Num d -> String -> Int
You can have multiple class constraints, so it's not clear how you would write them using your syntax; how would you write :
f :: (Eq d, Ord d) => d -> String -> Int ?
Also, you only have to tell the class constraint once, with your syntax
g :: (Num d) => d -> d -> d
would become:
g :: Num d -> Num d -> Num d
but that syntax lets you write nonsense :
h :: Num d -> Eq d -> Ord d
David.
David: Thanks for your input. As I have quite a distance to go with Haskell, I shall take all these comments under advisement. - Phil -
participants (3)
-
Brent Yorgey
-
David Virebayre
-
Philippe Sismondi