Ответ 1
Это опирается на теоретический результат, называемый "параметричностью", который сначала был определен Рейнольдсом, а затем разработан Уодлером (среди прочих). Пожалуй, самая известная статья на эту тему - "Теоремы бесплатно!" Вадлер.
Основная идея заключается в том, что только из полиморфного типа функции мы можем получить некоторую информацию о семантике функции. Например:
foo :: a -> a
Только из этого типа мы можем видеть, что, если foo
завершается, это - функция тождества. Интуитивно понятно, что foo
не может различать различные a
поскольку в Haskell у нас нет, например, Java instanceof
который может проверять фактический тип времени выполнения. Так же,
bar :: a -> b -> a
должен вернуть первый аргумент. И baz :: a → a → a
должен возвращать либо первое, либо второе. И quz :: a → (a → a) → a
должны применить некоторое фиксированное число раз, которое функция использовала к первому аргументу. Вы, наверное, поняли идею сейчас.
Общее свойство, которое может быть выведено из типа, довольно сложно, но, к счастью, оно может быть вычислено механически. В теории категорий это связано с понятием естественной трансформации.
Для типа map
мы получаем следующее страшное свойство:
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t3,t4 in TYPES, g :: t3 -> t4.
forall p :: t1 -> t3.
forall q :: t2 -> t4.
(forall x :: t1. g (p x) = q (f x))
==> (forall y :: [t1].
map_{t3}_{t4} g (map2_{t1}_{t3} p y) =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
Выше map
является известной функцией map, а map2
- любая произвольная функция, имеющая тип (a → b) → [a] → [b]
.
Теперь предположим, что map2
удовлетворяет законам функторов, в частности map2 id = id
. Затем мы можем выбрать p = id
и t3 = t1
. Мы получаем
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t2 -> t4.
(forall x :: t1. g x = q (f x))
==> (forall y :: [t1].
map_{t1}_{t4} g (map2_{t1}_{t1} id y) =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
Применение закона функторов на map2
:
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t2 -> t4.
(forall x :: t1. g x = q (f x))
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
Теперь давайте выберем t2 = t1
и f = id
:
forall t1 in TYPES.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t1 -> t4.
(forall x :: t1. g x = q x)
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} q (map_{t1}_{t1} id y))
По закону функтора map
:
forall t1, t4 in TYPES.
forall g :: t1 -> t4, q :: t1 -> t4.
g = q
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} q y)
что значит
forall t1, t4 in TYPES.
forall g :: t1 -> t4.
(forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} g y)
что значит
forall t1, t4 in TYPES.
map_{t1}_{t4} = map2_{t1}_{t4}
Подводя итог:
Если map2
- это любая функция с полиморфным типом (a → b) → [a] → [b]
и такая, что она удовлетворяет первому закону функтора map2 id = id
, тогда map2
должна быть эквивалентна стандартной функции map
.
Также см. Сообщение в блоге Эдварда Кметта.
Обратите внимание, что в Scala вышеприведенное верно только в том случае, если вы не используете x.isInstanceOf[]
и другие инструменты отражения, которые могут нарушить параметричность.