2013-03-19 70 views
2

从我的previous question,我一直在试图找出一些单子代码。首先,这里是我使用一个状态机功能:Haskell“无法推论”,“非类型变量参数”

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,我认为这是MonadError后面的String。如果我删除了类型声明,则代替Could not deduce。我明白启用FlexibleContexts只是“修复”这个,但是有没有更简单的方法可以让我抛出错误?我宁愿不启用各种编译器扩展。

Full code here

+5

'FlexibleContexts'是一个非常无害的扩展。没必要害怕这一点。如果没有类型签名,它还会编译您是否禁用单态限制。 – 2013-03-19 17:06:14

+1

如果不需要扩展,只是对扩展有点不利。我宁愿寻找替代方法,例如重构代码。解决方案1是FlexibleContexts。还有其他建议吗? – me2 2013-03-19 17:51:09

+0

或多或少,你要得到的所有解决方案都将是“打开FlexibleContexts”。不要害怕扩展,他们会帮助你。而这一个并不神秘。这是专门让你做你想做的。没有更多,没有更多。 – Carl 2013-03-19 18:05:24

回答

4

如果你绝对不希望使用FlexibleContextsNoMonomorphismRestriction,可以使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

4

为了阐述Daniel的答案,他的解决方案实际上是避免FlexibleContexts的一般解决方案。

你有这样一个约束的任何时间:

(SomeTypeConstructor SomeType) => ... 

...其中SomeType是触发FlexibleInstances警告一些具体的类型,你总是可以通过类型分级解决FlexibleContexts的操作要使用上SomeType,如:

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

...然后结合IsSomeType到你的约束:

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

...仅使用IsSomeType中的方法。