2

从我之前的问题中,我一直在尝试制定一些单子代码。首先,这是我正在使用的状态机功能:

import Control.Monad
import Control.Monad.Error

newtype FSM m = FSM { unFSM :: String -> m (String, FSM m) } 

fsm f []     = return []
fsm f (r:rs) = do
    (xs, f') <- unFSM f r  
    liftM (xs:) (fsm f' rs) 

现在,这编译得很好:

exclaim :: (Monad m) => FSM m
exclaim = FSM exclaim'
exclaim' xs = return (xs ++ "!", exclaim)

但这不是,因为类型声明:

question :: (MonadError String m) => FSM m
question = FSM question'
question' xs 
    | last xs == '?' = throwError "Already a question"
    | otherwise      = return (xs ++ "?", question)

错误是Non type-variable argument,我认为是指String之后MonadError。如果我删除类型声明,我会得到Could not deduce。我知道启用 FlexibleContexts 只是“修复”了这个问题,但是我可以做一些更简单的事情来让我抛出错误吗?我宁愿不启用各种编译器扩展。

完整代码在这里

4

2 回答 2

4

如果您绝对不想使用FlexibleContextsor NoMonomorphismRestriction,您可以在不打开模块中的扩展的情况下进行questionquestion'通用的编译以使其编译:

question :: (Error e, MonadError e m) => FSM m
question = FSM question'

question' :: (Error e, MonadError e m) => String -> m (String, FSM m)
question' xs
    | last xs == '?' = throwError $ strMsg "Already a question"
    | otherwise      = return (xs ++ "?", question)

Error通过使用,使其抛出一个通用类型strMsg,并指定类型签名。

但是,我仍然更喜欢启用FlexibleContexts.

于 2013-03-19T18:57:17.290 回答
4

详细说明丹尼尔的答案,他的解决方案实际上是避免的一般解决方案FlexibleContexts

任何时候你有一个约束,比如:

(SomeTypeConstructor SomeType) => ...

...SomeType触发FlexibleInstances警告的具体类型在哪里,您始终可以FlexibleContexts通过对要使用的操作进行类型分类来解决SomeType,例如:

class IsSomeType t where
    get :: t -> SomeType
    set :: SomeType -> t -> t

...然后纳入IsSomeType您的约束:

(IsSomeType t, SomeTypeConstructor t) => ...

...并且仅使用IsSomeType.

于 2013-03-19T20:20:29.440 回答