Я работаю над вопросами 99 Haskell и увидел решение для поиска последнего элемента списка:
myLast = foldr1 (const id)
тип const
равен a -> b -> a
, а const id
- b -> a -> a
так что здесь волшебство?
Я работаю над вопросами 99 Haskell и увидел решение для поиска последнего элемента списка:
myLast = foldr1 (const id)
тип const
равен a -> b -> a
, а const id
- b -> a -> a
так что здесь волшебство?
Тип id
- c->c
; он просто возвращает то же самое, что и ему.
Тип const
- a->b->a
. В const id
, a
становится c->c
, поэтому тип const в этом случае становится:
(c->c) -> b -> (c->c)
Теперь, когда вы применили эту функцию const, т.е. вы передали ей id
, вы остаетесь с b -> (c->c)
.
PS: Тип const const id
равен a->b->a
, а тип const const const id
равен b->a->a
и т.д.!
Никакой магии. Определение const
:
const :: a -> b -> a
const x y = x
И определение id
:
id :: a -> a
id x = x
Итак const id
- это просто \y -> id
, функция, которая всегда возвращает id
. И если id
просто \x -> x
, то const id
должен быть таким же, как \y -> \x -> x
. Эрго, он имеет тип b -> (a -> a)
.
const id
также можно записать flip const
. Поскольку const
есть \x -> \y -> x
, то flip const
принимает аргументы в обратном порядке, \y -> \x -> x
, что совпадает с const id
.
Вот мое понимание того, как это работает.
Это самое простое объяснение, о котором я мог думать, я попытался (специально) избежать любых потенциально запутанных понятий или слов.
Важное понятие, которое следует иметь в виду, - это частичное применение.
То, как я это понимаю, заключается в том, что в Haskell мы можем "исправить" один из параметров функции до известного значения, поэтому теперь функция принимает один меньший параметр.
Recap: тип const:
const :: a -> b -> a
В качестве более простого примера, пусть "исправить" первый параметр будет (+)
let f = const (+)
Теперь, когда мы зафиксировали первый параметр const, "f" - это функция, которая принимает только один параметр. Но поскольку const всегда возвращает свой первый параметр, который мы исправили, это означает, что все, что мы передаем в "f", будет проигнорировано, и оно всегда будет возвращать функцию "+" .
В качестве примера все следующие будут давать 5, так как f всегда будет возвращать функцию "+" независимо от того, что она получила в качестве первого параметра, а функция "+" будет работать на 2 и 3:
f "aaa" 2 3
f 904212 2 3
f undefined 2 3
Тип f:
f :: b -> Integer -> Integer -> Integer
Обратите внимание, что "b" может быть любым, как можно видеть выше.
Теперь для актуальной функции:
g = const id
Это означает, что мы "фиксировали" id как первый параметр, так же, как и выше, "g" игнорирует свой первый параметр и возвращает функцию "id". В Haskell мы можем продолжить и предоставить дополнительный параметр "id", как и мы (+) выше, так что, например, все они вернут 42:
g "aaa" 42
g undefined 42
Итак, "g" - это функция, принимающая два параметра и всегда возвращающая вторую, поэтому ее тип:
g = const id :: b -> a -> a
Но подождите минуту, я просто совершил гигантский прыжок. Не должен быть тип:
b -> (a -> a)
так как мы только что сказали, что принимаем что угодно и возвращаем "id"?
Хорошо, да, кроме того, что в Haskell эти круглые скобки можно опустить.
Это также имеет смысл, поскольку вы можете сразу предоставить функцию "id" с дополнительным параметром, который ему нужен (как в примере "const (+)" выше).
Единственный способ, которым я, наконец, понял это, - представить последовательность шагов, которые Haskell берет при оценке выражения типа const id 1 2
. Сначала рассмотрим следующие утверждения:
В Haskell все функции считаются curried: То есть все функции в Haskell принимают только один аргумент. 1
Функциональное приложение, тем не менее, ассоциируется слева: f x y действительно (f x) y. 1
Имея это в виду и видя сверху, что const
принимает два аргумента и id
принимает один, мы можем увидеть следующие шаги:
const id 1 2 -- Haskell evaluates 'const id' and returns a function (we can call it
-- 'constId') which accepts one argument and always returns 'id'
constId 1 2 -- Haskell now evaluates 'constId 1', which as expected just returns 'id'
id 2 -- 'id' is now applied to 2, which just returns '2'
2 -- final result
Отказ от ответственности: я новичок в Haskell.