9

モナドを実装する次のコードがあります。後で、より複雑なロジックでフィールドの設定を簡素化するためにそれを使用しようとしています。

data Rec = Rec {
    alpha :: Int,
    beta  :: Double,
} deriving (Show)
defaultRec = Rec 0 0 0

data Record r = Record { runRecord :: Rec -> (Rec, r) }
instance Monad Record where
    return r = Record $ \s -> (s, r)
    a >>= b  = Record $ \s -> let (q, r) = runRecord a s in runRecord (b r) q

createRecord f = fst $ runRecord f defaultRec

changeAlpha x  = Record $ \s -> (s { alpha = x }, ())

私は次のようなコードを使用します:

myRecord = createRecord (changeAlpha 9)

このコードは機能しますが、changeAlpha関数を単純化するためにTemplateHaskellを使用したいと思います。このようなものがあれば素晴らしいでしょう:

changeBeta x = $(makeChange beta) x

今、私はこれまで行ってきました:

changeBeta x = Record $ $([| \z -> \s -> (s { beta = z }, ()) |]) x

しかし、私がこれに変更するとすぐに:

changeBeta f x = Record $ $([| \z -> \s -> (s { f = z }, ()) |]) x

私はこれを手に入れます:

TestTH.hs:21:49: `f' is not a (visible) constructor field name

バリエーションはありません。これは可能ですか?

4

3 に答える 3

6

問題は、タイプ、式、または宣言のリストしかスプライスできないことです。レコードフィールドラベルはどちらでもないため、THコンビネーターを使用してタイプの式を作成し、Q Expそれをスプライスする必要がありますが、残りの部分には引き続きオックスフォードブラケットを使用できます。

makeChange :: Name -> Q Exp
makeChange x = [|
    \z -> Record $ \s -> ( $(recUpdE [| s |] [fieldExp x [| z |]]), () ) |]

これを使用するには、変更するフィールドの名前を引用する必要があります。

changeBeta :: Double -> Record ()
changeBeta x = $(makeChange 'beta) x
于 2011-12-12T08:56:35.007 に答える
4

State(T)モナドとレンズを再発明しただけではありませんか?

http://hackage.haskell.org/packages/archive/data-lens/2.0.2/doc/html/Data-Lens-Strict.html

タイプの_で始まる各「レコード」のレンズを生成するdata-lens-templateコードがあります。

于 2011-12-12T16:03:55.560 に答える
1

fただの名前だと思います。引用符で囲まれたものだけでなく、環境からのものであるため、「引用符を外す」必要があり[| |]ます。この例をここで見つけました[リンク、セクション「10.1.1タプルから選択」を参照]。unquoteはを使用$しているので、最終的なコードは次のようになります。

changeField f = [| \z s -> s { $(varE f) = z } |]

残念ながら、私のバージョンのghc(7.0.3)は。について不平を言っているよう$です。(解析エラーが発生します。)この質問がもう少し注目されることを願っていますが、私には良いようです(おそらく無関係なモナド部分を削除します)。

于 2011-12-12T08:27:16.990 に答える