Usually when I create functions I don't know in which functions it is better to use typeclasses (Eq,Integral and etc.) because sometimes there is no need to use them like:
factorial :: Int -> Int
bla bla bla
bla bla bla
and
factorial :: (Integral a) => a -> a
bla bla bla
bla bla bla
I believe that the second one is just taking time and place
but in elem function it is important to write Eq (below elleme is elem)
elemme :: Eq a => a -> [a] -> Bool
y `elemme` [] = False
y `elemme` (x:xs) = if y == x then True else y `elemme` xs
Please advise me something about it. Thanks.
Typeclasses let you write more general functions. Haskell has great tools for letting you specify that a function works over all types, like
id :: a -> a
id a = a
But some things don't work over all types,
(==) :: Eq a => a -> a -> Bool
Without typeclasses, we'd either have to write a new == for each type,
eqInteger :: Integer -> Integer -> Bool
Or specify that all types are equatable and can be tested for equality. But then you could ask
id == const 1
So, when you find yourself wanting to specify that your function works over a subset of all types, go for typeclasses.
I often favor writing this more general functions even if I don't use them over more than one type since the more general the signature, the fewer options there are for the implementation, which makes it easier to know that I haven't done something silly like
id :: Integer -> Integer
id = (+1)
You're essentially stating that your function only requires X functions which makes it possible to get a compiler error for using the wrong function.
One of the benefits I get out of making my functions as general as possible is that it often allows for use cases I didn't think of before. When you start making things general, you start coming up with weird uses for your functions.
You go, "But what if this value is a function?" Or "What if I allow this for any functor – what would be the result for other functors? Is it useful?" As it turns out, it is in a lot of cases! And this is one of the reasons Haskell is so great, in my opinion. It is easy to "accidentally" create very reusable functions.
In other languages, you design functions for specific purposes and use them that way. In Haskell, you design functions for specific purposes and give them a general type signature, and suddenly they work for a ton of cases you didn't design them for.
@jozefg made a great point about the generality restricting the possible implementations. I just want to give that more of a spotlight, because it is actually a very powerful concept. With a few general function, you can actually be completely sure what the function does only based on the type signature, because there's only one possible implementation for that general signature.
I recently encountered the signature
mystery :: (a -> b -> c) -> (a -> b) -> a -> c
which is interesting because we can actually figure out which function it is based on what it does. We have
mystery :: (a -> b -> c) -> (a -> b) -> a -> c
mystery f g x = ...
and we need it to return a c value of some kind. The only way we can get a value of type c is by applying the function f to an a value and a b value. We already have a single a value – this is the x argument. So we can partially apply f x :: b -> c but we still need a b value to get the c value we want.
The solution, of course, is to apply g x to get a b value, which we then can stick into f, thus finally returning a c value. The description of this is a little complicated to follow, but if you work it out in your head you arrive at
mystery f g x = f x (g x)
which performs the same thing as the library function ap. (From Control.Applicative.)
It is very cool that you can figure out what a function does solely based on its type signature!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With