2017-03-28 8 views
4

私はエルムのモナドの欠如に苦しんでいます。 Elm(http://package.elm-lang.org/packages/folkertdev/elm-state/latest/State)のための状態モナドを実装する図書館は私をかなり助けました。Elmで結果と状態を結合するにはどうすればよいですか?

問題は、それぞれが1つしか持たないときに、結果のタイプと状態タイプが交互に入れ子になっている状況に遭遇したことです。

次のシグネチャで関数を記述しようとしましたが、不可能なように見えます。内部状態は、外部状態が評価されて初めてわかるためです。私は、戻り値に州内の検索結果を置けば

join : Result a (State s (Result a (State s x))) -> Result a (State s x) 

は、多分それは動作しますが、それは外の結果が Errである場合にはダミーの状態が生成されます。

正しいアイデアは、結果と状態の両方を作ることだと思います。ハスケルのモナド変圧器に精通している人が、このような問題をどのように解決するか、代替の解決法を提案する方法を説明できますか?ここで

は、問題が発生し、一箇所の大まかなバージョンです:

generateConstraints environment value 
    |> Result.map (State.map (\(value, valueC) -> 
    Result.map 
     (State.map2 (\this (body, bodyC) -> 
     (this 
     , valueC++ bodyC++ [(this, body)] 
     )) 
     freshTypevar) 
     (generateConstraints (extend environment name value) body)) 
) 
+1

モナドトランスは、同じタイプのトランスを混在させると苦労します。私は典型的にはモナド型メらを使用していますが、タイプが複数回現れると失敗します。そのためのソリューションは、必要な特定のコンテキストを新しいタイプにすることです。 –

+0

私は本当に同じモナドを何度も持っている必要はありません。私が含むコードスニペットは 'generateConstraints'の1つのケースです。これは' Result String(State Int(Type、List Constraint)) 'を返します。再帰はうまくいきますが、束縛のための制約生成とは異なるものがあります。 – Joonazan

+0

私はまだ状態を持ち合わせている可能性があり、誤っている可能性はありますが、例外的な結果をすべて外に出すことができたので、奇跡的に解決した例があります。 – Joonazan

答えて

2

私は両方のモナドを書くことになった。私は後に失敗する必要があるので、国家が触れる前に、失敗する能力を犠牲にしなければならなかった。

type alias Infer a = State Int (Result String a) 

infer : a -> Infer a 
infer x = 
    State.state (Ok x) 

map : (a -> value) -> Infer a -> Infer value 
map f x = 
    State.map (Result.map f) x 

andThen : (a -> Infer b) -> Infer a -> Infer b 
andThen f x = 
    State.andThen 
    (\r -> case r of 
     Ok v -> f v 
     Err e -> State.state <| Err e 
    ) 
    x 

andMap : Infer y -> Infer (y -> z) -> Infer z 
andMap y = 
    andThen (\g -> map g y) 

map2 : (a -> b -> c) -> Infer a -> Infer b -> Infer c 
map2 f x y = 
    map f x 
    |> andMap y 

map3 : (a -> b -> c -> d) -> Infer a -> Infer b -> Infer c -> Infer d 
map3 f a b c = 
    map2 f a b 
    |> andMap c 

map4 : (a -> b -> c -> d -> e) -> Infer a -> Infer b -> Infer c -> Infer d -> Infer e 
map4 f a b c d = 
    map3 f a b c 
    |> andMap d 
3

Haskellのモナド変圧器に精通している誰かが、彼らはこの種の問題を解決したり、別の解決策を提案する方法を説明できますか?

まあ、私は少なくとも試みることができます。これはあなたのタイプが直接ハスケルに翻訳どのように見えるかです:

type EffM a s x = Either a (State s x) 

むしろ明白な観察は、それはモナド変換子ではないということです。

type TransM a s x = EitherT a (State s) x 

あなたが見ることができるように、唯一の変更はTxが括弧の外にあるという事実である:のように変圧器がどのように見えるかだ。後者の部分は変圧器のアプローチを理解する上で不可欠です。

StateEitherの結果が「成功」または「失敗」になるかどうかにかかわらず結果生成の一部ですが、「失敗」を生成する場合は状態操作が行われないことを意味します。私はそれが実際に何を意味するのか難しく考える必要だろうが、直感的に変圧器のアプローチは、典型的な、不可欠コードを取り扱う際には、つもりだ心を持っているものです。

あなたは、このような変圧器を使用する場合さて、joinが実際にモナドインターフェイスにフィッティングの結果として、無償で提供されています。

import Control.Monad.State 
import Control.Monad.Trans.Either 
import Control.Monad 

type Eff e s a = EitherT e (State s) a 

-- type the following in REPL 

λ :t join 
join :: Monad m => m (m a) -> m a 

λ :t join :: Eff e s (Eff e s a) -> Eff e s a 
join :: Eff e s (Eff e s a) -> Eff e s a 
    :: Eff e s (Eff e s a) -> Eff e s a 

-- the slightly cryptic output above means that it typechecks correctly 

だから、これはHaskellのがそれを解決する方法です。それぞれの変圧器のためのjoinの実装はどうなるのか、ミラーリング、 - 今、それは(より自然な感じ、私は個人的にStateEitherに例のすべてのもの2を反転したいaltough)の専門EitherStateタイプをこのように書くことが明らかに可能です。私はエルムで具体的にEitherTと書くことが可能かどうか分かりません。


一つの可能​​なアプローチ。他の再帰スキーム/ Freeが、今後の数年間に見られるかもしれません。エフェクトスタッキングの固有の順序付けは、当初よりも問題になることが判明しています。

それは少なくとも(Eitherは明らかに特殊ケースの一方として作用することができるため)xはモナドインスタンスに直接タイプすることができないという意味で、また、Monadありません。

+1

あなたは少し遅すぎました。私はすでに問題を自分で解決しました。私の場合、どちらの国家が働くのだろうか?少なくとも私はそれを書いた方法で、それは他の方法でしか動作しません。 – Joonazan

+0

'join'は'> = 'とほとんど同じですので無料で利用できます。また、私と彼の中には、非常に醜い 'Err e - > State.state <| Err e'。私はエルムで少なくとも "Err e - > x"は型チェックをしないので、そうしなければならないと思います。 – Joonazan

+1

@文章あなたのコメントを見ましたが、とにかく答えるかもしれないと思いました。あなたのケースではうまくいくと思います。これらの変圧器の順番は重要ではありません。そしてはい、 'join'は単純に' join x = x >> = id'です。あなたの実装の "醜さ"は、両方のレイヤーを同時にアンラップしなければならないという事実から成り立ちますが、トランスはそれを分割して後で作図することができます。 –

関連する問題