Разница между (а -> а) и а -> а - PullRequest
5 голосов
/ 04 мая 2011

Я заметил, что (хотя мне однажды сказали, что (a -> a) и a -> a означают одно и то же), я получаю сообщения об ошибках при использовании (a -> a).Должен ли я использовать (a -> a) только при использовании скобок среди типов?(т.е. (5 + 3) вместо 5 + 3)?Просто не совсем уверен, когда это необходимо

Ответы [ 5 ]

14 голосов
/ 04 мая 2011

(a -> a) и a -> a одинаковы одни ,

ff :: (a -> a)   -- this compiles
ff = id

gg :: a -> a
gg = id

h :: a -> a -> Bool
h _ _ = True

i = h ff gg   -- this compiles => ff & gg are of the same type.

, но будет отличаться в сочетании с большим количеством типов, таких как:

 a -> a  -> b
(a -> a) -> b

Это потому, что -> является ассоциативным справа, поэтому a -> a -> b на самом деле означает a -> (a -> b) (взять a и вернуть функцию), что отличается от (a -> a) -> b (взять функцию и вернуть b).

Это похоже на (1+2)*3 отличается от 1+2*3.

4 голосов
/ 04 мая 2011

Заключение в скобки устраняет неоднозначность нескольких конструкций в Haskell, когда другая информация, доступная компилятору, не помогает:

Приложение связывается слева

, поэтому вы можете опустить пареныпо аргументам функции.

Выражения, включающие инфиксные операторы, устраняются неоднозначностью из-за фиксированности оператора.

, поэтому во многих случаях нет необходимости в скобках с двоичными операторами различной фиксированности.

Последовательные операторы без скобок с одинаковым приоритетом должны быть либо левыми, либо правыми, чтобы избежать синтаксической ошибки.

и, наконец,

Даноне заключенное в скобки выражение x op y op z, скобки должны быть добавлены в x op y или y op z, если только не соблюдены определенные условия о приоритете

Мой общий совет, если приведенные выше утверждения не имеют никакого смысла: заключите в скобки вещи, пока не выучите правила.Или изучите Отчет по Хаскеллу очень усердно.

2 голосов
/ 04 мая 2011

Рассмотрим выражение 10 / 2 / 5. Это то же самое, что (10 / 2) / 5 или 10 / (2 / 5)? Если вы интерпретируете / как математическое деление, то первое верно, а второе ложно. Итак, вы видите, ответ на ваш вопрос «есть разница, но только иногда».

Типы противоположны. a -> b -> c совпадает с a -> (b -> c) и определенно не совпадает с (a -> b) -> c.

Вы говорите, что не совсем уверены, когда это необходимо: ну, вот оно. Если аргумент вашей функции также является функцией, то это необходимо.

Рассмотрим map :: (a -> b) -> [a] -> [b]. Это отличается от a -> b -> [a] -> [b], как видите, (a -> b) указывает на определенный тип функции: функцию от типа a до типа b.

iterate :: (a -> a) -> a -> [a] интереснее. Эта функция требует, чтобы типы входа и выхода функции в первом параметре были одинаковыми.

Вас может заинтересовать чтение карри и частичное применение. Один хороший ресурс среди многих: Узнайте о Haskell # Curried Functions

1 голос
/ 04 мая 2011

Это имеет значение только тогда, когда вы делаете функции более высокого порядка. Например:

f :: a -> a -> b

- это функция, которая ожидает два аргумента типа a и возвращает значение типа b, например,

f 2 2
f True True

Но функция

f :: (a -> a) -> b

Ожидает функцию в качестве аргумента. Единственный раз, когда a -> a и (a -> a) совпадают, если они являются единственным аргументом в выводе типа, как здесь

f :: (a -> a)
-- Same type as
f :: a -> a

Правила () при наборе текста почти такие же, как и в обычных выражениях. Это как выражения на уровень выше.

0 голосов
/ 04 мая 2011

Они одинаковы. Не могли бы вы описать ошибки, которые вы получаете, когда используете (a -> a)? У меня это нормально работает с ghci-7.0.3:

Prelude> let f :: (a -> a); f = id
Prelude> f "foo"
"foo"
Prelude>

В общем, вам нужно использовать скобки в типах, когда вы используете функции в качестве аргументов. Например, map :: (a -> b) -> [a] -> [b]. Без паренов это означало бы что-то еще.

...