
Hi Jan,
Looks like the monomorphism restriction to me. This article [1] is a
great explanation of this quirk.
[1] http://lambda.jstolarek.com/2012/05/towards-understanding-haskells-monomorph...
There are two solutions:
1. Add {-# LANGUAGE NoMonomorphismRestriction #-} to your code.
2. Give each binding explicit arguments:
process = ... -- as before
where
m >>= k = m E.>>= k
m >> n = m E.>> n
return x = E.return x
Since the monomorphism restriction doesn't apply to declarations
with arguments, this change should make the bindings polymorphic
again.
Hope this helps.
On Wed, Oct 28, 2015 at 3:14 AM, Jan Bracker
Hello,
I am currently playing around with RebindableSyntax and having several bind/return/sequence functions in scope at the same time. I thought that it would be enough to just pick the right one to use in each do-block by using a "where" or a "let". Surprisingly, I get some type related issues I can only fix by adding in some type signatures, but I don't understand why these signatures are actually necessary.
Here is my example program:
{-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-}
import Prelude import qualified Prelude as P import qualified Control.Effect as E import Control.Effect.State
ifThenElse :: Bool -> a -> a -> a ifThenElse True t e = t ifThenElse False t e = e
main :: IO () main = do return () where return = P.return
data Tree = Leaf Int | Branch Tree Tree
process :: Tree -> State '[ "flatten" :-> Bool :! 'R ] (Either Tree [Int]) process (Leaf i) = do flatten <- get (Var :: (Var "flatten")) if flatten then return $ Right [i] else return $ Left $ Leaf i where --(>>=) :: (E.Inv State f g) => State f a -> (a -> State g b) -> State (E.Plus State f g) b (>>=) = (E.>>=) (>>) :: (E.Inv State f g) => State f a -> State g b -> State (E.Plus State f g) b (>>) = (E.>>) return = E.return fail = E.fail process (Branch tl tr) = do eitherL <- process tl eitherR <- process tr case (eitherL, eitherR) of (Left l, Left r) -> return $ Left $ Branch l r (Right l, Right r) -> return $ Right $ l ++ r where (>>=) :: (E.Inv State f g) => State f a -> (a -> State g b) -> State (E.Plus State f g) b (>>=) = (E.>>=) (>>) :: (E.Inv State f g) => State f a -> State g b -> State (E.Plus State f g) b (>>) = (E.>>) return = E.return fail = E.fail
The program uses the "effect-monad" package in version 0.6.1.
1) The type signatures in the "where" following each do-block of the "process" function are required. If I remove the type signature of the sequence functions I get a type error of the following nature:
examples/effect/Test.hs:33:16: Could not deduce (E.Inv m0 f0 g0) arising from a use of ‘E.>>’ Relevant bindings include (>>) :: m0 f0 a -> m0 g0 b -> m0 (E.Plus m0 f0 g0) b (bound at examples/effect/Test.hs:33:9) In the expression: (E.>>) In an equation for ‘>>’: (>>) = (E.>>) In an equation for ‘process’: process (Leaf i) = do { flatten <- get (Var :: Var "flatten"); if flatten then return $ Right [...] else return $ Left $ Leaf i } where (>>=) = (E.>>=) (>>) = (E.>>) return = E.return fail = E.fail
examples/effect/Test.hs:33:16: No instance for (E.Effect m0) arising from a use of ‘E.>>’ In the expression: (E.>>) In an equation for ‘>>’: (>>) = (E.>>) In an equation for ‘process’: process (Leaf i) = do { flatten <- get (Var :: Var "flatten"); if flatten then return $ Right [...] else return $ Left $ Leaf i } where (>>=) = (E.>>=) (>>) = (E.>>) return = E.return fail = E.fail
Which I interpret as the inability to infer the "E.Effect" and "E.Inv" constraints that are implied by the use of "E.>>". But why can't those constraints be inferred correctly? Shouldn't a definition like "(>>) = (E.>>)" just propagate the type signature and specialize it as needed?
2) If I remove the type signature for the bind operation, I get the following type error:
examples/effect/Test.hs:38:3: Couldn't match type ‘'["flatten" :-> (Bool :! 'R)]’ with ‘'[]’ Expected type: State '["flatten" :-> (Bool :! 'R)] (Either Tree [Int]) -> (Either Tree [Int] -> State '[] (Either Tree [Int])) -> State '[] (Either Tree [Int]) Actual type: State '["flatten" :-> (Bool :! 'R)] (Either Tree [Int]) -> (Either Tree [Int] -> State '[] (Either Tree [Int])) -> State (E.Plus State '["flatten" :-> (Bool :! 'R)] '[]) (Either Tree [Int]) In a stmt of a 'do' block: eitherR <- process tr In the expression: do { eitherL <- process tl; eitherR <- process tr; case (eitherL, eitherR) of { (Left l, Left r) -> return $ Left $ Branch l r (Right l, Right r) -> return $ Right $ l ++ r } } In an equation for ‘process’: process (Branch tl tr) = do { eitherL <- process tl; eitherR <- process tr; case (eitherL, eitherR) of { (Left l, Left r) -> return $ Left $ Branch l r (Right l, Right r) -> return $ Right $ l ++ r } } where (>>=) = (E.>>=) (>>) :: (E.Inv State f g) => State f a -> State g b -> State (E.Plus State f g) b (>>) = (E.>>) return = E.return
Which tells me that GHC is expecting the wrong type, but inferring the correct type. Again I don't see why this wrong type is expected and the right type is ignored.
In either case, why does adding or removing the type signature for the local definitions make a difference at all? I suspect the issue has to do with the language extensions I enabled or the type-level computations that are done by the "effect-monad" package, but I cannot find a satisfying answer. Does anybody have a good explanation?
I am working with GHC 7.10.2.
Best, Jan
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- Chris Wong (https://lambda.xyz) "I fear that Haskell is doomed to succeed." -- Tony Hoare