laci37
laci37

Reputation: 490

Haskell still can't deduce type equality

This question is a follow up on the question Haskell can't deduce type equality. I have tried implementing a basic polynomial approximator using the Trainable type described in my previous question with the following code:

module MachineLearning.Polynomial
       (polynomial,
        polynomialTrf
       ) where
import MachineLearning.Training
import MachineLearning.Utils

polynomialTrf :: Floating n => [n] -> n -> n
polynomialTrf coeff var = helper 0 coeff var 0 where
  helper _ [] var acc = acc
  helper 0 (c:cs) var acc = helper 1 cs var c 
  helper deg (c:cs) var acc = helper (deg+1) cs var (acc+(c*(var^deg)))

polynomialCost :: Floating n => n -> n -> [n] -> n
polynomialCost var target coeff = sqcost (polynomialTrf coeff var) target

polynomialSV :: (Floating n) => Trainable n n
polynomialSV = Trainable polynomialTrf polynomialCost

Here sqcost is just sqcost a b = (a-b) ^ 2. I get the following error message from the compiler:

src/MachineLearning/Polynomial.hs:18:26:
    Could not deduce (n1 ~ n)
    from the context (Floating n)
      bound by the type signature for
                 polynomialSV :: Floating n => Trainable n n
      at src/MachineLearning/Polynomial.hs:18:1-53
    or from (Floating n1)
      bound by a type expected by the context:
                 Floating n1 => [n1] -> n -> n
      at src/MachineLearning/Polynomial.hs:18:16-53
      `n1' is a rigid type variable bound by
           a type expected by the context: Floating n1 => [n1] -> n -> n
           at src/MachineLearning/Polynomial.hs:18:16
      `n' is a rigid type variable bound by
          the type signature for polynomialSV :: Floating n => Trainable n n
          at src/MachineLearning/Polynomial.hs:18:1
    Expected type: [n] -> n1 -> n1
      Actual type: [n] -> n -> n
    In the first argument of `Trainable', namely `polynomialTrf'
    In the expression: Trainable polynomialTrf polynomialCost

src/MachineLearning/Polynomial.hs:18:40:
    Could not deduce (n ~ n1)
    from the context (Floating n)
      bound by the type signature for
                 polynomialSV :: Floating n => Trainable n n
      at src/MachineLearning/Polynomial.hs:18:1-53
    or from (Floating n1)
      bound by a type expected by the context:
                 Floating n1 => n -> n -> [n1] -> n1
      at src/MachineLearning/Polynomial.hs:18:16-53
      `n' is a rigid type variable bound by
          the type signature for polynomialSV :: Floating n => Trainable n n
          at src/MachineLearning/Polynomial.hs:18:1
      `n1' is a rigid type variable bound by
           a type expected by the context: Floating n1 => n -> n -> [n1] -> n1
           at src/MachineLearning/Polynomial.hs:18:16
    Expected type: n -> n -> [n1] -> n1
      Actual type: n -> n -> [n] -> n
    In the second argument of `Trainable', namely `polynomialCost'
    In the expression: Trainable polynomialTrf polynomialCost

My question is where is the problem coming from? How can I solve it? For me it feels clear the those two types are equal, so there is the possibility that I misunderstand something in the type system.

Upvotes: 0

Views: 263

Answers (1)

Daniel Fischer
Daniel Fischer

Reputation: 183978

I'm afraid the rank 2 type

data Trainable a b
    = Trainable (forall n. Floating n => [n] -> a -> b)
                (forall n. Floating n => a -> b -> [n] -> n)

won't help you. I concentrated only on the type error in the other question, and didn't notice that Floating isn't rich enough to make that really usable. Since you can't generically convert from a Floating type to some other type or to a Floating type from anything but Reals (realToFrac), you can't write many interesting functions of the given polymorphic types in general, sorry.

The problem here is that the above type demands that the functions passed to the Trainable constructor work for all Floating types n (whatever the specified a and b), but the implementations polynomialTrf and polynomialCost only work for one specific Floating type, the one that is given as (both) the parameters. polynomialTrf has the type Floating n => [n] -> n -> n, but it would need to have the type (Floating n, Floating f) => [f] -> n -> n to be eligible to be passed to the Trainable constructor.

With a third type parameter, you would have

Trainable polynomialTrf polynomialCost :: Floating n => Trainable n n n

and for trainSgdFull, you would need the type

trainSgdFull :: (Floating n, Ord n, Mode s) => Trainable a b (AD s n) -> [n] -> a -> b -> [[n]]

to be able to use gradientDescent.

I don't know how a proper solution to your problem might look like.

Upvotes: 2

Related Questions