Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning an instance of a class in a Haskell Function [duplicate]

If the return of a function is a class ClassA, is it possible to return in such function any instance of ClassA? Ex: someFunction :: (ClassA a) => String -> a

So, why this function below does not work? Note that String is an instance of Eq

getAnyEq :: (Eq a) => String -> a
getAnyEq input  |input == "1" = "something"
                |otherwise = "other"

The error that occurs is:

Could not deduce (a ~ [Char])
from the context (Eq a)
  bound by the type signature for getAnyEq :: Eq a => String -> a
  at src/InterceptorRegistry.hs:11:13-33
  `a' is a rigid type variable bound by
      the type signature for getAnyEq :: Eq a => String -> a
      at src/InterceptorRegistry.hs:11:13

I've tried to find this exact explanation on Internet resources but i did not find...could you show me some?

like image 223
andregps Avatar asked Mar 02 '14 21:03

andregps


3 Answers

The type Eq a => a does not mean "A type which implements Eq", but rather "Any type that implements Eq. For example, if you implement your function using undefined:

getAnyEq :: (Eq a) => String -> a
getAnyEq str = undefined

The following functions compile correctly (although will crash with a undefined error at runtime):

x,y,z :: Bool
x = getAnyEq "test" == "hello"
y = getAnyEq "test" == [Just (Right True)]
z = getAnyEq "test" == ("this", "sss")

It isn't possible to give a decent implementation of your function, as there is no way of generating the value for the result.

A function that returns a type variable only makes sense when the type variable has an instance of a class which contains a function that returns the value. Eg consider the Num class:

class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

(note I was testing this on a pretty old version of ghc, your Num may not have Eq or Show constraints).

The function fromInteger returns an a (without needed an a as input), so we can get an a from that type class. The other functions can be used once you have a value. So the following function works:

getANum:: (Num a) => String -> a
getANum "zero" = fromInteger 0
getANum "asdf" = fromInteger 46
getANum _ = fromInteger 1

> getANum "asdf"
46

Note that as a literal integer is effectively parsed as fromInteger <num>, the fromInteger function calls in the above function aren't actually necessary. I just included them to show how it works.

Other common type classes which can be used to retrieve a value are:

  • Monad (using return)
  • Applicative (using pure)
  • Monoid (using mempty)
  • Read (using read or any other of its other functions)
like image 64
David Miani Avatar answered Oct 17 '22 06:10

David Miani


In addition to @David Miani's wonderful answer, I'd also add that every function type declaration in standard Haskell type system implies a forall (or ∀) quantifier:

getAnyEq :: (Eq a) => String -> a

is semantically equivalent to

getAnyEq :: forall a . (Eq a) => String -> a

which you can try with the {-# LANGUAGE ExplicitForall #-} extension. That means, literally, that for each type a constrained with the type class Eq there is a function getAnyEq with the given type. However, you propose the definition for a single type (which is String) only, not forall.

I suggest that your definition would be valid with another quantifier, ∃:

getAnyEq :: exists a . (Eq a) => String -> a

It's not implemented by the GHC, but for example the obsolete UHC (Utrecht Haskell Compiler) supports it. Unfortunately, I can't currently try it.

like image 4
Yuuri Avatar answered Oct 17 '22 06:10

Yuuri


After reading the answers above and the related topic linked on the top of this page, i've concluded that the solution is the using of Existentially Quantified Types. So, the solution to my getAnyEq function is:

{-# LANGUAGE ExistentialQuantification #-}
data ShowEq = forall s. Eq s => SE s 

getAnyEq :: String -> ShowEq
getAnyEq input  |input == "1" = SE "ds"
                |otherwise = SE "ss"

A very useful link that explains these types is: http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types

like image 2
andregps Avatar answered Oct 17 '22 07:10

andregps