У нас есть
f :: a -> b
g :: (a -> r) -> r
h :: b -> r
и нам нужно
_1 :: r
Есть два способа сделать r
: g
и h
.
Давайте попробуем использовать h
.h
принимает аргумент типа b
.Единственный способ получить один из них - использовать f
.f
принимает аргумент типа a
, и ... у нас нет никакого способа получить один из них.
Так что теперь давайте попробуем использовать g
вместо:
mapReader f g h = g _2
Нам говорят
_2 :: a -> r
Поскольку мы создаем функцию, мы можем применять лямбда-абстракцию как обычно:
mapReader f g h = g (\a -> _3)
a :: a
_3 :: r
Но подождите ... теперь мы имеют и a
, поэтому мы можем вернуться к нашей первой попытке:
mapReader f g h = g (\a -> h (f a))
или, более компактно,
mapReader f g h = g (h . f)
Что если вместо этогочтобы вернуться к первой попытке, мы сделали это вторым путем снова ?
mapReader' f g h =
g (\a1 -> g (\a2 -> _4))
_4 :: r
Вы можете идти этим путем вечно, но вы также можете остановиться здесь двумя различными способами:
mapReader2 f g h =
g (\_ -> g (h . f))
mapReader3 f g h =
g (\a1 -> g (\_ -> h (f a1)))
Ой!Это три разные функции, которые имеют одинаковый тип, и, как показано, этот подход можно использовать для генерации бесконечного семейства функций!Как вы можете решить, какой вы хотите?Вы должны рассмотреть намерение.Аргумент g
является продолжением, поэтому вы хотите составить функцию с тем, что вы передаете g
, а не вызывать g
несколько раз.Таким образом, mapReader
является «правильным» ответом.
Точнее, mapReader
должен отображать морфизмы для продолжения функтор .Это требует, в частности, что
mapReader id = id
То есть
mapReader id g h = g (h . id)
= g h
Это безусловно верно для правильного определения, но не для любого другого.