0 votes
I am currently trying to wrap my head around typeclasses and instances and I don't quite understand the point of them yet. I have two questions on the matter so far:

1) Why is it necessary to have a type class in a function signature when the function uses some function from that type class. Example:

f :: (Eq a) => a -> a -> Bool
f a b = a == b
Why put (Eq a) in the signature. If == is not defined for a then why not just throw the error when encountering a == b? What is the point in having to declare the type class ahead?

2) How are type classes and function overloading related?

It is not possible to do this:

data A = A
data B = B

f :: A -> A
f a = a

f :: B -> B
f b = b
But it is possible to do this:

data A = A
data B = B

class F a where
  f :: a -> a

instance F A where
  f a = a

instance F B where
  f b = b
What is up with that? Why can't I have two functions with the same name but operating on different types... Coming from C++ I find that very strange. But I probably have wrong conceptions about what these things really are. but once I wrap them in these type class instance thingies I can.

Feel free to hurl category or type theoretical words at me as well, as I am learning about these subjects in parallel to learning Haskell and I suspect there is a theoretical basis in these for how Haskell does things here.
asked Dec 30, 2017 in Core java by Amrendra | 25 views

1 Answer

0 votes
I agree with much of Willem Van Onsem’s answer, but I think it overlooks one of the principal advantages of typeclasses over truly ad-hoc overloading: abstraction. Imagine we used ad-hoc overloading instead of typeclasses to define the Monad operations:

-- Maybe
pure :: a -> Maybe a
pure = Just

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Just x >>= f = f x
Nothing >>= _ = Nothing

-- Either
pure :: a -> Either e a
pure = Right

(>>=) :: Either e a -> (a -> Either e b) -> Either e b
Right x >>= f = f x
Left err >>= _ = Left err
Now, we know that every monad can be expressed in terms of pure and >>=, as above, but we also know that they can be equivalently expressed using fmap, pure, and join. Therefore, we should be able to implement a join function that works on any monad:

join x = x >>= id
However, now we have a problem. What is join’s type?

Clearly, join has to be polymorphic, since it works on any monad by design. But giving it the type signature forall m a. m (m a) -> m a would obviously be wrong, since it doesn’t work for all types, only monadic ones. Therefore, we need something in our type that expresses the need for the existence of some operation (>>=) :: m a -> (a -> m b) -> m b, which is exactly what the typeclass constraint provides.

Given this, it becomes clear that ad-hoc overloading makes it possible to overload names, but it is impossible to abstract over those overloaded names because there is no guarantee the different implementations are related in any way. You could define monads without typeclasses, but then you couldn’t define join, when, unless, mapM, sequence, and all the other nice things that you get for free when you define just two operations.
answered Dec 30, 2017 by Amrendra

Related Questions

Welcome to Knowledge Boostr, where you can ask questions and receive answers from other members of the community.
This is a collaboratively edited question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top Q&A for professional and enthusiast programmers Q&A for professional and enthusiast programmers Q&A for professional and enthusiast programmers Q&A for professional and enthusiast programmers

23,494 questions
40,169 answers
23,610 users