私はこの記事(Joseph Abrahamson による fpcomplete の Lenses from Scratch) が非常に優れていることを発見しました。それはあなたが始めたレンズの同じ表現から始まり、その構成を定義し、レンズにもっと似た表現への道に沿って続きます。
編集:この種のことを行う場合、タイプホールが優れていることがわかります:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (_,_)
これで 2 つの穴ができました。タプルの最初の部分には (出力が消去されました) と書かれています。
Found hole ‘_’ with type: x -> z
...
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
(<.>) :: Lens y z -> Lens x y -> Lens x z
バインディングをよく見ると、必要なものはすでに揃っています。getB :: x -> y
そしてgetA :: y -> z
関数合成と一緒に(.) :: (b -> c) -> (a -> b) -> a -> c
したがって、喜んでこれを挿入します。
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, _)
そして、次のような 2 番目のタイプの穴に進みます。
Found hole ‘_’ with type: z -> x -> x
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
私たちが持っている最も類似したものはsetA :: z -> y -> y
、ラムダを挿入して引数をキャプチャすることから始めます。
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> _)
タイプホールを次のように変更します。
Found hole ‘_’ with type: x
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
型チェックを挿入することはできx
ますが、必要なものは得られません (設定しても何も起こりません)。を与えることができる唯一の他のバインディングx
は issetB
であるため、それを挿入します。
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB _ _)
最初のタイプの穴は次のように述べています。
Found hole ‘_’ with type: y
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
したがって、y が必要で、スコープ内にあるものを見て、を与えると a を与えることgetB
ができますが、これはたまたま持っていますが、これは役に立たないレンズにつながり、再び何もしません。別の方法は、次を使用することです。y
x
setA
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA _ _) _)
(ここから少し速度を上げていきます) ここでも、最初の穴はz
、ラムダへの引数としてたまたま持っている型の何かを必要としています。
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z _) _)
type の最初の型の穴を埋めるために、ラムダの引数を指定してy
使用できます。getB :: x -> y
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) _)
これにより、残りのタイプ ホールが 1 つ残ります。これは、簡単に に置き換えることができx
、最終的な定義につながります。
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) x)
id
必要に応じてタイプ ホールとフーグルを使用して、自分で定義を試みることができます。