flexible contexts problem

I'm having trouble understanding the following behavior. The following program compiles: {-# OPTIONS_GHC -XMultiParamTypeClasses -XFlexibleContexts #-} import Control.Monad.State class Has α s where has :: s -> (α, s) project :: (MonadState s m, Has α s) => m α project = do (α, s) <- gets has put s return α f :: (MonadState s m, Has Int s) => m Int f = do x <- project return x However, if you replace the function f with f :: (MonadState s m, Has Int s) => m Int f = do x <- project y <- project return x then it fails with the error Could not deduce (Has α s) from the context (MonadState s m, Has Int s) arising from a use of `project' at /Users/sean/uploads/Weird.hs:16:12-18 Possible fix: add (Has α s) to the context of the type signature for `f' In a stmt of a 'do' expression: y <- project In the expression: do x <- project y <- project return x In the definition of `f': f = do x <- project y <- project return x I don't see how the second call to project could possibly make a difference. Could someone please tell me what I'm doing wrong? Thanks in advance. Sean

2009/9/12 Sean McLaughlin
I'm having trouble understanding the following behavior. The following program compiles: {-# OPTIONS_GHC -XMultiParamTypeClasses -XFlexibleContexts #-} import Control.Monad.State class Has α s where has :: s -> (α, s) project :: (MonadState s m, Has α s) => m α project = do (α, s) <- gets has put s return α f :: (MonadState s m, Has Int s) => m Int f = do x <- project return x However, if you replace the function f with f :: (MonadState s m, Has Int s) => m Int f = do x <- project y <- project return x then it fails with the error Could not deduce (Has α s) from the context (MonadState s m, Has Int s) arising from a use of `project' at /Users/sean/uploads/Weird.hs:16:12-18 Possible fix: add (Has α s) to the context of the type signature for `f' In a stmt of a 'do' expression: y <- project In the expression: do x <- project y <- project return x In the definition of `f': f = do x <- project y <- project return x
I don't see how the second call to project could possibly make a difference. Could someone please tell me what I'm doing wrong?
Look at the type signature of project: project :: (MonadState s m, Has α s) => m α The only way it can know what Has instance to use is by knowing α. And the only way to know α is to know the return type. You never use y in your new f, which is the only thing that could have told the compiler what type α was for the second project call (it's not necessarily the same as in the first). Possible solutions: * Use fundeps to constrain Has so there is only one choice of α for each choice of s. Do this only if this constraint correctly models the meaning of Has (which is... what?) * Give the compiler some way of knowing what type y is, eg. f :: (MonadState s m, Has Int s) => m Int f = do x <- project y :: Int <- project return x Returning it or taking something of its type as a parameter (constraining with asTypeOf) is just as good, then a constraint can be added to the context. Luke

On Sep 12, 2009, at 20:17 , Sean McLaughlin wrote:
However, if you replace the function f with
f :: (MonadState s m, Has Int s) => m Int f = do x <- project y <- project return x
then it fails with the error
Could not deduce (Has α s) from the context (MonadState s m, Has Int s) arising from a use of `project' at /Users/sean/uploads/Weird.hs:16:12-18 Possible fix: add (Has α s) to the context of the type signature for `f' In a stmt of a 'do' expression: y <- project In the expression: do x <- project y <- project return x In the definition of `f': f = do x <- project y <- project return x
I think what this is really telling you is that there's no way for ghc to tell what type y has. x is fine because it's in the result (hence fixed by the type (m Int)), but y is just "there" and all ghc knows is that it has (Has α s) and (MonadState s m, Has Int s) but no way to get from one to the other (more specifically: it has no clue what α might be). Note that project is explicitly polymorphic, so the fact that you used it to get an (Int) does not in any sense guarantee that the subsequent use *also* produces an Int. If you want this, you'll have to constrain the call somehow. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Ah, I see. Thanks very much. For some reason I figured the second type would be resolved to Int, but now I see that is totally wrong. Best, Sean On Sat, Sep 12, 2009 at 8:34 PM, Brandon S. Allbery KF8NH < allbery@ece.cmu.edu> wrote:
On Sep 12, 2009, at 20:17 , Sean McLaughlin wrote:
However, if you replace the function f with
f :: (MonadState s m, Has Int s) => m Int f = do x <- project y <- project return x
then it fails with the error
Could not deduce (Has α s) from the context (MonadState s m, Has Int s) arising from a use of `project' at /Users/sean/uploads/Weird.hs:16:12-18 Possible fix: add (Has α s) to the context of the type signature for `f' In a stmt of a 'do' expression: y <- project In the expression: do x <- project y <- project return x In the definition of `f': f = do x <- project y <- project return x
I think what this is really telling you is that there's no way for ghc to tell what type y has. x is fine because it's in the result (hence fixed by the type (m Int)), but y is just "there" and all ghc knows is that it has (Has α s) and (MonadState s m, Has Int s) but no way to get from one to the other (more specifically: it has no clue what α might be).
Note that project is explicitly polymorphic, so the fact that you used it to get an (Int) does not in any sense guarantee that the subsequent use *also* produces an Int. If you want this, you'll have to constrain the call somehow.
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
participants (3)
-
Brandon S. Allbery KF8NH
-
Luke Palmer
-
Sean McLaughlin