2017-02-01 6 views
1

私はいくつかの特殊なフォームだけで普通の木に非常に似ているいくつかのデータ型を持っています。ネストされたフィールドをレンズで初期化する便利な方法

data NestedTree = NT 
    { _dummy :: Int 
    , _tree :: HashMap String NestedTree 
    } deriving (Show) 

makeLenses ''NestedTree 

レンズを使用してデータ型のインスタンスを必須に初期化したいとします。

example :: NestedTree 
example = flip execState (NT 0 mempty) $ do 
    dummy .= 3 
    tree.at "foo" ?= flip execState (NT 0 mempty) (dummy .= 10) 

あなたは私が(NT 3 mempty)との最初(NT 0 mempty)を置き換えることができ、この例で観察することができますが、これはポイントではありません。ここで私は今、得たものです。私が望むのは、この素敵な命令的なスタイルを使ってネストされたHashMapを初期化することもできます。より正確な、私はこのような何かを書くことができるようにしたい:

example :: NestedTree 
example = flip execState (NT 0 mempty) $ do 
    dummy .= 3 
    tree.at "foo" ?= flip execState (NT 0 mempty) $ do 
     dummy .= 10 
     tree.at "foo nested" ?= NT 5 mempty 
    tree.at "bar" ?= flip execState (NT 0 mempty) $ do 
     dummy .= 15 
     tree.at "bar nested" ?= NT (-3) mempty 

私の実際のデータ構造はより複雑であり、それは非常にすぐに単純なレコードを使用して、それを初期化するために、本当に醜いとなります。だから私はある種のDSLを使いたいと思っています。しかし、上記のコードはコンパイルされていないことに気付くことができます。

($)の優先順位が最も低く、私はtree.at "foo" ?= flip execState (NT 0 mempty) $ doと書くことはできません。しかし、実際には入れ子になったdoの周りに()を追加したくありません。

このような関数を書くために、任意の演算子を$doと混在させる良い方法はありますか?私は本当にwordsAssign = (?=)のようないくつかのヘルパーを紹介し、私は?=オペレータが好きなので

wordsAssign (tree.at "foo") $ flip execState (NT 0 mempty) $ do

のような関数を呼び出す必要はありません。たぶん私は間違ったことをやっているのですが、私がやりたいこのようなことは、手作業で書かれた演算子を使ってレンズなしで行うことができますか?

答えて

3

zoomは、ネスト状態の更新を処理するために調整されています。あなたのケースでは、残念ながら、Maybe -nessは少しぎこちない、それを使用します:あなたは、外側のレベルで新しいキーを挿入する必要がなかった場合

example :: NestedTree 
example = flip execState (NT 0 mempty) $ do 
    dummy .= 3 
    zoom (tree.at "foo") $ do 
     put (Just (NT 0 mempty)) 
     _Just.dummy .= 10 
     _Just.tree.at "foo nested" ?= NT 5 mempty 
    -- Or, using zoom one more time: 
    zoom (tree.at "bar") $ do 
     put (Just (NT 0 mempty)) 
     zoom _Just $ do 
      dummy .= 15 
      tree.at "bar nested" ?= NT (-3) mempty 

比較のために、あなたが使用することができるだろうixの代わりに、atとすべてMaybe関連の定型ドロップ:

zoom (tree.ix "foo") $ do 
     dummy .= 10 
     tree.at "foo nested" ?= NT 5 mempty 
+2

感謝を!私はこのアプローチを試み、それは魔法のように機能します。もし誰かが興味があれば:ズーム機能(https://github.com/serokell/log-warper/blob/91d82ed1b3a331ed4a8e0f458390f985028d1817/src/System/Wlog/LoggerConfig.hs#L120)で私を助けるユーティリティ機能、そしてここで私がどのように使用するか(https ://github.com/serokell/log-warper/blob/91d82ed1b3a331ed4a8e0f458390f985028d1817/test/Test/Wlog/RollingSpec.hs#L69) – Shersh

2

あなたは彼らが一緒に素晴らしいプレー作るために$と同じ優先順位を使用して独自の?=を定義することができます。

import Control.Lens hiding ((?=)) 
import qualified Control.Lens as L 

(?=) 
    :: MonadState s m 
    => ASetter s s a (Maybe b) -> b -> m() 
(?=) = (L.?=) 

infixr 0 ?= 

これはあなたの例の作品で。

関連する問題