Search code examples
haskellfunctor

Haskell : Couldn't match expected type ‘a -> a’ with actual type ‘Failable (a -> a)’


I'm trying to experiment with functors and safe division in Haskell, but I got this little error, and I can't really figure out why.

here goes my code :

module Main
where

data Failable a = Failure String | Success a

instance (Show a) => Show (Failable a) where
    show (Failure s) = "Failure : " ++ s
    show (Success x) = "Success : " ++ (show x)

instance Functor Failable where
    fmap f (Success x) = Success (f x)
    fmap _ (Failure s) = Failure s

(//) :: (Num a) => Failable a -> Failable a -> Failable a
_ // (Success 0) = Failure "Division by zero"
x // y = fmap (fmap (/) x) y

main = do
    print $ (Success 1) // (Success 2)

And the output :

main.hs:16:16:
Couldn't match expected type ‘a -> a’
            with actual type ‘Failable (a -> a)’
Relevant bindings include
  y :: Failable a (bound at main.hs:16:6)
  x :: Failable a (bound at main.hs:16:1)
  (//) :: Failable a -> Failable a -> Failable a
    (bound at main.hs:15:1)
Possible cause: ‘fmap’ is applied to too many arguments
In the first argument of ‘fmap’, namely ‘(fmap (/) x)’
In the expression: fmap (fmap (/) x) y

Solution

  • I think you are trying to lift (/) to the Failable functor. You could achieve that using pattern matching, but I think you want to do it using the Functor functions (fmap), alone.

    This is however impossible. At best, fmap (/) can provide

    (/)      :: Num a => a          ->           a -> a
    fmap (/) :: Num a => Failable a -> Failable (a -> a)
    

    and we have no general way to turn that ugly Failable (a -> a) into Failable a -> Failable a.

    This is why we have a more powerful type class: Applicative, for doing precisely that

    (<*>) :: Applicative f => f (a -> b) -> f a -> f b
    

    So... congratulations. You have just discovered the issue for which such a type class was introduced.

    I'd recommend you write a Applicative instance. After that, (omitting the zero check)

    (//) = liftA2 (/)
    -- or, (this being the most popular style, nowadays)
    x // y = (/) <$> x <*> y
    -- or,
    x // y = fmap (/) x <*> y