KannaKiski
KannaKiski

Reputation: 1

Haskell class misunderstanding

Hello i need to create a class which contains a method called prize' which calculates some Fractional. I want to instance in case I get an Order, later I will have to instance again in case I get a [Order], this is the code :

class Ped a where
    prize' :: Fractional b => a -> b

instance (Integral n, Fractional p) => Ped (Order n p) where
    prize' x = prizeOrder x

data (Integral c,Fractional p) => Product c p
    = Prod c String p
    deriving (Show, Read) 

data (Integral n, Fractional p) => Order n p
    = PdMult (Product n p) n
    | PdUnit (Product n p)
    deriving (Show, Eq)

prize :: (Fractional p, Integral c) =>(Product c p) -> p
prize (Prod _ _ x) = x

prizeOrder :: (Fractional p, Integral c) => (Order c p) -> p
prizeOrder (PdMult p n) = (prize p) * (fromIntegral n)
prizeOrder (PdUnit p) = prize p

Prelude says:

Could not deduce (p ~ b)
from the context (Integral n, Fractional p)
bound by the instance declaration
at src\Funciones.hs:6:10-55
or from (Fractional b)
bound by the type signature for
     prize' :: Fractional b => Order n p -> b
at src\Funciones.hs:7:5-11
`p' is a rigid type variable bound by
  the instance declaration
  at src\Funciones.hs:6:10
`b' is a rigid type variable bound by
  the type signature for prize' :: Fractional b => Order n p -> b
  at src\Funciones.hs:7:5
Expected type: Order b
  Actual type: Order n p
Relevant bindings include
  x :: Order n p
    (bound at src\Funciones.hs:7:13)
  prize' :: Order n p -> b
    (bound at src\Funciones.hs:7:5)
In the first argument of `prizeOrder', namely `x'
In the expression: prizeOrder x

Upvotes: 0

Views: 137

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476584

The problem is that your class definition gives an additional degree of freedom, your instance fails to provide. In your class you state:

class Ped a where
    prize' :: Fractional b => a -> b

That means that the programmer could choose whatever Fraction b for b he/she wants.

Now if we look to your instance:

instance (Integral n, Fractional p) => Ped (Order n p) where
    prize' x = prizeOrder x

Here you say that prize' depends on prizeOrder. The prizeOrder function has however the signature:

prizeOrder :: (Fractional p, Integral c) => (Order c p) -> p

Which means you cannot choose b at all. If I would use prize' with as expected return type Float, that would be accepted by Ped, but not by a datastructure Order Double Int.

You can "solve" this by using multi-parameter classes, you take the b in the class signature:

class Ped a b where
    prize' :: a -> b

Next you need to define the instance with a second type parameter to ensure the output is p:

instance (Integral n, Fractional p) => Ped (Order n p) p where
    prize' x = prizeOrder x

You will need to activate some additional GHC features, the full code would read like:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DatatypeContexts #-}

class Ped a b where
    prize' :: a -> b

instance (Integral n, Fractional p) => Ped (Order n p) p where
    prize' x = prizeOrder x

data (Integral c,Fractional p) => Product c p
    = Prod c String p
    deriving (Show, Read) 

data (Integral n, Fractional p) => Order n p
    = PdMult (Product n p) n
    | PdUnit (Product n p)
    deriving (Show)

prize :: (Fractional p, Integral c) =>(Product c p) -> p
prize (Prod _ _ x) = x

prizeOrder :: (Fractional p, Integral c) => (Order c p) -> p
prizeOrder (PdMult p n) = (prize p) * (fromIntegral n)
prizeOrder (PdUnit p) = prize p

But as said by @bheklilr, don't use "class constraints on data types". Furthermore one better asks his/herself whether it pays off to use a class.

Upvotes: 1

Related Questions