haskelltypeclassfunctor

Showing Functor result in Haskell


I have defined the following Functor with the following type class ...

-- Let's suppose we want to create a functor that
-- performs the opperation :
--
-- fmap inc (Strength 3)

data Stat a = Strength a | Agility a | Constitution a
          deriving Show

instance Functor Stat where
  -- fmap :: (a -> b) -> Stat a -> Stat b
  fmap g (Strength str)     = Strength (g str)
  fmap g (Agility agi)      = Agility (g agi)
  fmap g (Constitution con) = Constitution (g con)

-- Then it can be called as fmap inc (Strength 3)

-- Let's suppose I use a Type class for this function

class Inc a where
  incStat :: (Num a) => a -> a

instance Inc (Stat a) where
  incStat (Strength a) = Strength a

-- instance Show a => Show (Stat a) where
--   show (Strength a) = show (a)
--   show (Agility a) = show (a)
--   show (Constitution a) = show (a) 

It builds ok, but when I try to do the following command

fmap incStat (Strength 3)

I'm getting the following error I don´t fully understand

<interactive>:62:1: error:
• Ambiguous type variable ‘b0’ arising from a use of ‘print’
  prevents the constraint ‘(Show b0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘b0’ should be.
  Potentially matching instances:
    instance Show Ordering -- Defined in ‘GHC.Show’
    instance Show a => Show (Stat a)
      -- Defined at characteristicsFunctor.hs:9:24
    ...plus 26 others
    ...plus 12 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it

Can you help me ? What is the error saying ?

NOTE : take IncStat as identity . I`m just checking the structure of the script


Solution

  • incStat already understands your functor, it does not need fmap to teach it how. So you may either use incStat directly, without fmap:

    > incState (Strength 3)
    Strength 3
    

    or you may use fmap directly, without incStat:

    > fmap (1+) (Strength 3)
    Strength 4
    

    To encapsulate this version you may like to define incStat in terms of fmap:

    instance Num a => Inc (Stat a) where
        incStat = fmap (1+)
    

    If you insist on using both incStat and fmap, then you must have an outer functor for the fmap to operate on -- which, if you like, may again be Stat.

    > fmap incStat [Strength 3, Strength 4]
    [Strength 3, Strength 4]
    > fmap incStat (Agility (Strength 3))
    Agility (Strength 3)
    

    As an aside, the Num constraint in your Inc declaration is almost certainly a mistake. I recommend deleting it.

    class Inc a where incStat :: a -> a
    

    The Num constraints are better placed on the instances, where they are used.

    instance Num a => Inc (Stat a) where
        incState (Strength a) = Strength (1+a)