Haskell: приложение-функция с $

В следующем фрагменте вы можете увидеть мои две функции collatz, которые я написал в Haskell. Для рекурсивного приложения я использовал круглые скобки в первом примере (collatz), чтобы получить правильный приоритет.

Как я только что изучил функциональное приложение с $, я попытался переписать функцию (collatz '), используя эту вещь. Однако я сталкиваюсь со следующей ошибкой:

Не удалось сопоставить ожидаемый тип `[a] '        против inferred type `a1 → [a1] 'Во втором аргументе` (:)', а именно `collatz '' В первом аргументе` ($) ', а именно `n: collatz '' В выражении: n: collatz '$ n `div` 2

collatz :: (Integral a) => a -> [a]

collatz 1 = [1]

collatz n | even n    = n : collatz (n `div` 2)
          | otherwise = n : collatz (n * 3 + 1)

collatz' :: (Integral a) => a -> [a]

collatz' 1 = [1]

collatz' n | even n    = n : collatz' $ n `div` 2
           | otherwise = n : collatz' $ n * 3 + 1

Мне показалось странным, что это не сработало. Поэтому я попробовал аналогичный пример:

True : [even $ 3 `div` 3]

Я был бы признателен, если бы кто-нибудь мог взглянуть на него и сказать мне, что я делаю неправильно.

Ответ 1

$ имеет более низкий приоритет, чем : (а также все остальное), поэтому ваша функция анализируется как

(n : collatz') $ (n `div` 2)

Это приведет к ошибке вашего типа. Второй аргумент : ожидает список, но вместо этого вы передаете функцию collatz.

Если вы все еще хотите избежать скобок вокруг части 3n + 1, вы можете сделать что-то вроде следующего

(n:) . collatz' $ n `div` 2
n : (collatz' $ n `div` 2)

хотя они не обязательно более чистые, чем оригинал. В случае, если вам интересно, (n:) в первом примере представляет собой синтаксический сахар для \x -> n : x

Ответ 2

Поскольку другие объяснили, в чем проблема, я подумал, что объясню, как вы могли разобраться в этом сами. (Обучение человека рыбе и т.д.)

Обратите внимание на эту часть сообщения об ошибке:

В первом аргументе '($)', а именно 'n: collatz' '

Чтобы понять, что это проблема приоритета. GHC сообщает вам, что n : collatz' анализировался как первый аргумент $, в то время как вы ожидали, что первый аргумент будет просто collatz'.

В этот момент я обычно запускаю GHCi и проверяю приоритеты, связанные с командой :info:

> :info :
data [] a = ... | a : [a]   -- Defined in GHC.Types
infixr 5 :
> :info $
($) :: (a -> b) -> a -> b   -- Defined in GHC.Base
infixr 0 $

В нем говорится, что предварительная оценка : равна 5, а приоритет $ равен 0, что объясняет, почему : является обязательным "более жестким", чем $.

Ответ 3

: связывается сильнее, чем $. Рассмотрим

Prelude> let f x = [x]
Prelude> 1 : f 2
[1,2]
Prelude> 1 : f $ 2

<interactive>:1:5:
    Couldn't match expected type `[a0]' with actual type `t0 -> [t0]'
    In the second argument of `(:)', namely `f'
    In the expression: 1 : f
    In the expression: 1 : f $ 2

Обратите внимание на "выражение" 1 : f, найденное парсером; он видит (1 : f) $ 2, а не 1 : (f $ 2).

Ответ 4

Как указано в @missingno, это проблема приоритета оператора. Вы можете переписать его так:

collatz' n | even n    = n : (collatz' $ n `div` 2)
           | otherwise = n : (collatz' $ n * 3 + 1)

Но это явно не покупает вас много, потому что у вас все еще есть скобки.