Как / почему работают определения функций в Haskell с функциями LHS и RHS? - PullRequest
2 голосов
/ 21 июня 2020

Я изучаю Haskell из книги Хаттона «Программирование на Haskell» (2-е изд.). В начале гл. 7, «Функции высшего порядка», функция «дважды» определяется как:

twice :: (a -> a) -> a -> a
twice f x = f (f x)

Затем приводится несколько примеров использования, например:

> twice reverse [1,2,3]
[1,2,3]

Затем автор говорит что «тот факт, что реверсирование (конечного) списка дважды не имеет никакого эффекта, фиксируется уравнением дважды reverse = id ...» Когда я запускаю ghci и пишу:

GHCi, version 8.4.4: http://www.haskell.org/ghc/  :? for help
Prelude> :type twice

<interactive>:1:1: error: Variable not in scope: twice
Prelude> twice reverse = id
Prelude> :type twice
twice :: p -> a -> a
Prelude> twice reverse [1,2,3]
[1,2,3]

Фактически это работает как определение функции, поскольку дважды не определено ранее. Теперь мой вопрос, как это работает? дважды обратный просто определен как псевдоним для id ? Но если да, то почему я могу отдельно запрашивать тип дважды ? В каких частях отчета на языке Haskell объясняется этот вид синтаксиса определения функций? Кроме того, почему используется переменная типа p ? Обычно используются a, b et c. .

Edit

Спасибо за полезные ответы и комментарии, пока!

Я просто хочу предоставить немного больше информации по другим вопросам, которые я задал, но которые не были решены. Конечно, на самые важные вопросы уже даны ответы.

Почему одна из переменных типа называется p?

Давайте посмотрим на следующий сеанс ghci:

GHCi, version 8.4.4: http://www.haskell.org/ghc/  :? for help
Prelude> twice asdf fsda = id
Prelude> :type twice
twice :: p1 -> p2 -> a -> a
Prelude> mySum a b c d = a + c
Prelude> :type mySum
mySum :: Num a => a -> p1 -> a -> p2 -> a

Кажется, что p или p1, p2 et c. используются как общие типы c для переменных, которые не используются. Может быть, их тоже используют для других целей. Я нашел подсказку в статье на другом форуме вопросов и ответов. Имена, используемые для переменных типа generi c, могут даже зависеть от реализации c. Важный вывод: неважно, какие буквы используются, потому что важно то, что они отличаются.

Какие части спецификации языка являются соответствующими?

Возможно гл. 4.4.3, «Привязки функций и шаблонов», в отчете о языке 2010 Haskell. Но сейчас для меня это слишком много информации. В конце концов, я где-то в первой половине вводной Haskell книги.

1 Ответ

5 голосов
/ 21 июня 2020

Давайте рассмотрим, что вы здесь делаете. Сначала вы определяете:

twice :: (a -> a) -> a -> a
twice f x = f (f x)

Это обычное определение функции, работающее так, как вы ожидаете: twice (+1) 2 = 3, twice reverse "abc" = "abc" et c. Когда Хаттон говорит, что «тот факт, что реверсирование (конечного) списка дважды не имеет никакого эффекта, фиксируется уравнением twice reverse = id», он просто говорит, что twice reverse имеет тот же эффект, что и выполнение id.

Но затем вы определяете:

twice reverse = id

Это не делает то, что вы ожидаете! Вы можете ожидать, что это подтвердит, выполняется ли уравнение twice reverse = id или что-то подобное. Но вместо этого он определяет совершенно новую функцию twice, называет ее единственный аргумент reverse, а затем игнорирует этот аргумент и возвращает функцию id. Здесь вы используете shadowing : вы определяете новый идентификатор (в данном случае reverse) с тем же именем, что и старый идентификатор. Фактически, если вы включите все предупреждения с помощью -Wall, GH C предупредит вас об этом:

Prelude> :set -Wall
Prelude> twice reverse = id

<interactive>:2:7: warning: [-Wname-shadowing]
    This binding for `reverse' shadows the existing binding
      imported from `Prelude' (and originally defined in `GHC.List')

Таким образом, определение twice reverse = id ничем не отличается от определения функции twice myArg = id, других чем тот факт, что аргумент имеет другое имя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...