の正しい読みは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 の型a
をb
( で示されているようにa -> f b
) 変更できるため、パラメーターも必要ですt
。これは、大まかに言うと、「その内部でtos
を変更した後の型」を意味します。a
b
statusCode
Int
は、 に作用する関数を に作用する関数に変換するレンズですStatus
。
statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)
responseStatus
Status
a に作用する関数を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)
正確にどのように^.
機能するかはまだわかりません。これは、レンズのコア メカニクスと魔法に密接に結びついています。これについてはかなりの数の著作があるので、ここでは繰り返しません。紹介として、これとこれをご覧になることをお勧めします。また、この優れたビデオもご覧ください。