Ответ 1
Правильное показание r ^. responseStatus . statusCode
равно r ^. (responseStatus . statusCode)
. Это естественно, поскольку композиция функции возвращает функцию при применении к двум аргументам, поэтому (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
обозначает ее часть. Поскольку наша входная функция может изменить тип a
на b
(как указано a -> f b
), нам также нужен параметр t
, который примерно означает "тип s
после того, как мы изменили a
к b
внутри него".
statusCode
является линзой, которая преобразует функцию, действующую на a Int
к функции, действующей на a Status
:
statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)
responseStatus
преобразует функцию, действующую на a Status
к функции, действующей на 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)
Пока не видно, как работает ^.
. Он тесно связан с основным механиком и магией линз; Я не буду повторять его здесь, поскольку об этом написано немало работ. Для введения я рекомендую посмотреть этот и этот, и вы также можете смотреть это отличное видео.