Почему нет необходимости указывать параметр в этой функции?

Я довольно новичок в 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 это функция, чтобы признать, что

  1. reverse, map reverse, а также inits все функции:

    • map является функцией высшего порядка; он принимает одну функцию в качестве аргумента и возвращает другую функцию.

    • map reverse это функция, потому что map применяется к функции reverse,

  2. Композиция двух функций - это другая функция (предположим, что типы совпадают, так что мы можем сосредоточиться на результате каждой композиции, а не проверять, что каждая композиция проверяет тип).

таким образом

  • 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]], это должна быть функция, которая принимает список as в качестве аргумента и возвращает список списков as.

Другие вопросы по тегам