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を参照していると思います。型宣言を削除すると、代わりに取得します。FlexibleContextsを有効にすると、これが「修正」されることは理解していますが、エラーをスローできるようにするために、もっと簡単な方法がありますか?あらゆる種類のコンパイラ拡張機能を有効にしたくはありません。StringMonadErrorCould not deduce

ここに完全なコード。

4

2 に答える 2

4

ダニエルの答えを詳しく説明すると、彼の解決策は実際には回避するための一般的な解決策FlexibleContextsです。

次のような制約があるときはいつでも:

(SomeTypeConstructor SomeType) => ...

...警告SomeTypeをトリガーする具体的なタイプはどこにありますか。次のように、使用する操作をタイプ分類するFlexibleInstancesことで、いつでも回避できます。FlexibleContextsSomeType

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

...そしてIsSomeType制約に組み込む:

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

...そして。のメソッドのみを使用しIsSomeTypeます。

于 2013-03-19T20:20:29.440 に答える
4

FlexibleContextsまたはを絶対に使用したくない場合は、モジュールで拡張機能をオンにせずにコンパイルするように少し一般的にすることが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 に答える