11

A wreq tutorialを読んでいます:

レンズは、Haskell 値の一部に焦点を当てる方法を提供します。たとえば、Response型にはresponseStatusレンズがあり、サーバーから返されるステータス情報に焦点を当てています。

ghci> r ^. responseStatus
Status {statusCode = 200, statusMessage = "OK"}

この^.演算子は、最初の引数として値を、2 番目の引数としてレンズを取り、レンズによってフォーカスされた値の部分を返します。

関数合成を使用してレンズを構成します。これにより、深く入れ子になった構造の一部に簡単に焦点を合わせることができます。

ghci> r ^. responseStatus . statusCode
200

この引数の順序で行われる関数合成が、入れ子構造をその順序でどのように処理できるかを思いつくことはできません。

見てください:またはr ^. responseStatus . statusCodeのいずれr ^. (responseStatus . statusCode)(r ^. responseStatus) . statusCodeです。

最初のものは、最初に処理する関数を構築しstatusCode(レコードから取得しStatusますか?-表示された値から推測できるようにStatus {statusCode = 200, statusMessage = "OK"})、次にresponseStatus応答ステータスを処理する必要がある関数に渡します。つまり、逆です。実際には、ステータス コードは応答ステータスの一部です。

2番目の読み取りも、ステータスコードを最初に処理するため、意味がありません。

4

1 に答える 1

13

の正しい読みはr ^. responseStatus . statusCodeですr ^. (responseStatus . statusCode)。関数合成は 2 つの引数に適用されると関数を返すため、これは当然のことです。したがって(r ^. responseStatus) . statusCode、出力される可能性のある値とは対照的に、関数を返す必要があります。

これは、なぜレンズが「間違った」順序で構成するのかという疑問を残しています。レンズの実装はちょっと不思議なので、もっと簡単な例を見てみましょう。

firstペアの最初の要素をマップする関数です。

first :: (a -> b) -> (a, c) -> (b, c)
first f (a, b) = (f a, b)

何をしmap . firstますか?first最初の要素に作用する関数を受け取り、ペアに作用する関数を返します。これは、次のように型を括弧で囲むとより明確になります。

first :: (a -> b) -> ((a, c) -> (b, c))

また、次のタイプを思い出してmapください。

map :: (a -> b) -> ([a] -> [b])

map要素に作用する関数を受け取り、リストに作用する関数を返します。これで、f . g最初に適用gしてから結果を にフィードすることで機能しfます。したがってmap . first、ある要素型に作用する関数を取り、ペアに作用する関数に変換し、ペアのリストに作用する関数に変換します。

(map . first) :: (a -> b) -> [(a, c)] -> [(b, c)]

firstそしてmap両方とも、構造の一部に作用する機能を、構造全体に作用する機能に変えます。ではmap . first、全体の構造firstが の焦点になりmapます。

(map . first) (+10) [(0, 2), (3, 4)] == [(10, 2), (13, 4)]

次にレンズの種類を見てみましょう。

type Lens = forall f. Functor f => (a -> f b) -> (s -> f t)

Functorここではビットを無視してみてください。少し目を細めると、これは と の型に似ていmapますfirst。また、レンズが構造の一部に作用する機能を、構造全体に作用する機能に変換することも起こります。上記の署名でsは、構造全体をa示し、その一部を示しています。入力関数は to の型ab( で示されているようにa -> f b) 変更できるため、パラメーターも必要ですt。これは、大まかに言うと、「その内部でtosを変更した後の型」を意味します。ab

statusCodeIntは、 に作用する関数を に作用する関数に変換するレンズですStatus

statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)

responseStatusStatusa に作用する関数をa に作用する関数に変換しますResponse

responseStatus :: Functor f => (Status -> f Status) -> (Response -> f Response)

の型は、 でresponseStatus . statusCode見たのと同じパターンに従いますmap . first

responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response)

正確にどのように^.機能するかはまだわかりません。これは、レンズのコア メカニクスと魔法に密接に結びついています。これについてはかなりの数の著作があるので、ここでは繰り返しません。紹介として、これこれをご覧になることをお勧めします。また、この優れたビデオもご覧ください。

于 2015-03-08T18:25:48.067 に答える