2

I was trying to define a very simple data structure that is suppose to add an Infinity element to any type under Num. I also put it under a defined class NumContainer, which has a method fromNum that construct a NumWithInf using a regular Num. The code is really straight forward.

data NumWithInf a = Infinity | Finite a deriving Show

class NumContainer k where
    fromNum :: Num a => a -> k

instance Num a => NumContainer (NumWithInf a) where
    fromNum x = Finite x

However when I ran it, GHCI gave me the following error:

hw.hs:7:24:
    Could not deduce (a ~ a1)
    from the context (Num a)
      bound by the instance declaration at hw.hs:6:10-45
    or from (Num a1)
      bound by the type signature for
                 fromNum :: Num a1 => a1 -> NumWithInf a
      at hw.hs:7:5-24
      `a' is a rigid type variable bound by
          the instance declaration at hw.hs:6:10
      `a1' is a rigid type variable bound by
           the type signature for fromNum :: Num a1 => a1 -> NumWithInf a
           at hw.hs:7:5
    In the first argument of `Finite', namely `x'
    In the expression: Finite x
    In an equation for `fromNum': fromNum x = Finite x
Failed, modules loaded: none.

I understand that it says the x in Finite x doesn't necessarily have the same type as a in the signature fromNum :: Num a => a -> k. How should I specify this?

xzhu
  • 5,675
  • 4
  • 32
  • 52
  • 7
    Unsolicited design suggestion: see how far you can get without the class, I doubt you actually need it. – luqui Aug 31 '15 at 05:28

3 Answers3

5

A simple solution (i.e. one that does not resort to fancy type system trickery) is making your class work with types of kind * -> * rather than *. In practice, that just means you define it as:

class NumContainer k where
    fromNum :: Num a => a -> k a -- Result type parametrised on `a` 

The instance then becomes:

instance NumContainer NumWithInf where
    fromNum x = Finite x

Note that there is no place to put a Num constraint in the instance. That would be unnecessary anyway - the constraint in the type of fromNum is good enough already.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • Thanks. Is there a way to stop users from constructing `Finite 'a'`? I think for some reason Haskell doesn't let programmers restrict arguments of constructors. – xzhu Aug 31 '15 at 05:44
  • 2
    @trVoldemort The easiest way to stop users from constructing arbitrary `Finite a` is not exporting the `Finite` constructor from the module where you define it. For a much fancier way that probably is overkill for what you are doing, see [this answer](http://stackoverflow.com/a/7438716/2751851). (Curio: the question there was about the `DatatypeContexts` extension. It allowed writing `data Num a => NumWithInf a`; however, it was deprecated due to uselessness.) – duplode Aug 31 '15 at 05:57
2

The problem is you are trying to define fromNum from ANY Num to ANY other Num which is not possible. You would like something like

fromNum x = Finite (toNum x)

where toNum convert x to the correct Num. This is not possible in the general case. However, your container is in fact an applicative functor and your function fromNum (with a narrower signature a -> k a) is just pure.

Also you can define easily an Num instance for NumContainer and then you can implement fromInteger as

frommInteger a = Finite (fromInteger a)

The conversion is now possible because you are not starting from an arbitrary Num but from an Integer.

Please also not, that at the moment without just one Infinite your type is isomorph to Maybe.

mb14
  • 22,276
  • 7
  • 60
  • 102
0

Why do you need a typeclass ? A typeclass is usually needed to overload a function depending on the type. So unless you are planning to have different containers you probably don't need a typeclass and could just do

 fromNum :: Num a => a -> NumWithInf a
 fromNum = Finite

However, your question "How should I specify the type in a instance statement when there is a type class restriction?" you can do it using MultiParamTypeClasse and pass a as a parameter of your typeclass.

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

data NumWithInf a = Infinity | Finite a deriving Show

class NumContainer a k  where
       fromNum :: Num a => a -> k

instance Num a => NumContainer a (NumWithInf a) where
       fromNum x = Finite  x
mb14
  • 22,276
  • 7
  • 60
  • 102