Почему нет необходимости указывать параметр в этой функции?
Я довольно новичок в Haskell, и на этой неделе я нашел эту особенность в нескольких слайдах лекций. Я пытаюсь понять, почему следующая функция не должна включать параметр:
-- Return all final segments of the argument, longest first
-- (horrible runtime complexity, used here for illustration purposes only)
tails :: [a] -> [[a]]
tails = reverse . map reverse . inits . reverse
Если бы я назвал это как tails "thisisastring"
тогда это будет действительный аргумент. Разве нет необходимости указывать параметр, например tails xs = ...
, Все остальные функции, которые я видел раньше, были именно такими.
3 ответа
Параметр неявный. Или, по-другому, reverse . map reverse . inits . reverse
оценивает функцию типа [a] -> [[a]]
,
Рассмотрим более простой пример:
double_impl x = x * 2
double = double_impl
Тип double
здесь тот же тип, что и double_impl
т.е. принимает один параметр класса типов Num
:
main = do
print $ double_impl 5
print $ double 5
-- Out: 10
-- Out: 10
Это называется бессмысленным стилем (где "точка" - это математический термин, который здесь в основном означает "аргумент").
Четное tails xs = ...
это просто синтаксический сахар для tails = \xs -> ...
так что все, что вам нужно сделать, чтобы убедить себя в том, что tails
это функция, чтобы признать, что
reverse
,map reverse
, а такжеinits
все функции:map
является функцией высшего порядка; он принимает одну функцию в качестве аргумента и возвращает другую функцию.map reverse
это функция, потому чтоmap
применяется к функцииreverse
,
- Композиция двух функций - это другая функция (предположим, что типы совпадают, так что мы можем сосредоточиться на результате каждой композиции, а не проверять, что каждая композиция проверяет тип).
таким образом
reverse . map reverse
это функция,- так
reverse . map reverse . inits
это функция, - а также
reverse . map reverse . inits . reverse
это функция.
поскольку tails
присваивается значение reverse . map reverse . inits . reverse
, tails
сам по себе это тоже функция.
Мы это видим tails
это функция, проверяя ее тип.
Чтобы вычислить его тип, мы начнем с записи типов всех промежуточных функций в композиции. Обратите внимание, что мы используем переменные нового типа для каждого вхождения функции.
reverse :: [a] -> [a]
inits :: [b] -> [[b]]
map :: (c -> d) -> [c] -> [d]
Теперь у нас есть map reverse
имеет тип [[e]] -> [[e]]
так как мы получаем c=d=[e]
для какого-то типа e
от сравнения выражений
reverse :: c -> d -- for some c and d
reverse :: [e] -> [e] -- for some e
Следовательно, последние два промежуточных имеют типы
map reverse :: [[e]] -> [[e]]
reverse :: [f] -> [f]
Теперь мы начинаем пытаться сопоставить типы. Позвольте мне сначала подчеркнуть, что, очевидно, ЭТИ НЕ РЕАЛЬНЫЕ ВИДЫ! (извините за все заглавные буквы, но я не хочу, чтобы кто-то пропустил это.)
inits . reverse :: [a] -*- [a] = [b] -*> [[b]]
-- I'm using a -*- b -*> c to denote the type a -> c obtained by
-- composing a function of type a -> b with one of type b -> c.
-- The *s are to break the double dashes up,
-- so they aren't parsed as a comment.
-- Anyway, looking at this type, we see
-- we must have [a] = [b], so a = b
-- we can rewrite the type of inits . reverse as
inits . reverse :: [a] -> [[a]]
Тогда для следующей композиции:
map reverse . inits . reverse :: [a] -*- [[a]] = [[e]] -*> [[e]]
-- again, we have [[a]] = [[e]], so e = a, and we have
map reverse . inits . reverse :: [a] -> [[a]]
Наконец, мы имеем
reverse . map reverse . inits . reverse :: [a] -*- [[a]] = [f] -*> [f]
-- This time we have [[a]] = [f], so we must have f = [a], so the type
-- of the final composition is
tails = reverse . map reverse . inits . reverse :: [a] -> [[a]]
поскольку tails
имеет тип [a] -> [[a]]
, это должна быть функция, которая принимает список a
s в качестве аргумента и возвращает список списков a
s.