[Haskell-beginners] Monadic composition without throwing genericity under the bus?

Daniel Fischer daniel.is.fischer at web.de
Tue Feb 2 11:08:44 EST 2010


Am Dienstag 02 Februar 2010 16:06:52 schrieb Dave Bayer:
>  test1, test2 ∷ Monad m ⇒ m (a → a)
>   test1 = mcompose unit unit
>   test2 = compose  unit unit
>
> -- test2 error:
> -- Could not deduce (Composable
> -- (m (a -> a)) (m1 (a1 -> a1)) (m2 (a2 -> a2)))
> -- from the context (Monad m2)
> -- arising from a use of `compose' at Issue2.lhs:26:10-27
>
>   test3, test4 ∷ Maybe ShowS
>   test3 = mcompose tab tab
>   test4 = compose  tab tab
>
> It appears to me that type inference in type classes is broken. How else
> to explain why mcompose has no trouble  figuring out that the monads are
> the same, but compose is stumped?

Try removing the type signature for test2 and see what that gives:

Compose.hs:28:8:
    No instance for (Composable (m (a -> a)) (m1 (a1 -> a1)) c)
      arising from a use of `compose' at Compose.hs:28:8-25
    Possible fix:
      add an instance declaration for
      (Composable (m (a -> a)) (m1 (a1 -> a1)) c)
    In the expression: compose unit unit
    In the definition of `test2': test2 = compose unit unit
Failed, modules loaded: none.

commenting out test2 and querying the type at the prompt:

*Compose> :t compose unit unit
compose unit unit
  :: (Monad m,
      Monad m1,
      Composable (m (a -> a)) (m1 (a1 -> a1)) c) =>
     c


In mcompose, you specified exactly which types to use, in particular that 
there's only one monad involved.
In test2, the type checker must determine the types from scratch.

compose :: forall a b c. Composable a b c => a -> b -> c

test2 = compose unit unit

unit :: forall m a. Monad m => m (a -> a)

The type checker can't assume that both unit's in test2 have the same type, 
so we have two monads (m, m1) and two types (a, a1) which are to be 
composed to give a third type (c).

compose can only work when it knows the types of both arguments. It doesn't 
know the type of unit (since that's polymorphic), so it can't work with 
unit.

You can help it somewhat by adding more FunDeps to Composable,

class Composable a b c | a b → c, a c -> b, b c -> a where
    compose ∷ a → b → c

, then it can work when it knows
a) the types of both arguments
or
b) the type of one argument and the type of the result.

Doesn't help with test2, though.


More information about the Beginners mailing list