从我的previous question开始,我一直试图找出一些monadic代码。首先,这是我正在使用的状态机功能:
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只是“修复”了这个但是有什么更简单的我可以做以允许我抛出错误?我宁愿不启用各种编译器扩展。
完整代码here。
答案 0 :(得分:4)
如果您绝对不想使用FlexibleContexts
或NoMonomorphismRestriction
,则可以使question
和question'
更加通用,以便在不启用的情况下进行编译模块中的扩展程序:
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
。
答案 1 :(得分:4)
详细说明丹尼尔的答案,他的解决方案实际上是避免FlexibleContexts
的一般解决方案。
任何时候你都有一个约束:
(SomeTypeConstructor SomeType) => ...
...其中SomeType
是触发FlexibleInstances
警告的某种具体类型,您可以通过类型对要在{{1}上使用的操作进行分类来解决FlexibleContexts
},例如:
SomeType
...然后将class IsSomeType t where
get :: t -> SomeType
set :: SomeType -> t -> t
合并到约束中:
IsSomeType
...并仅使用(IsSomeType t, SomeTypeConstructor t) => ...
中的方法。