Применение функции: почему здесь используется $? - PullRequest
8 голосов
/ 11 января 2009

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

Кажется, я ошибался: (

Этот пример показан в руководстве:

instance Monad [] where
   xs >>= f = concat . map f $ xs

Не могу понять, почему там был использован $; ghci мне тоже не помогает, так как даже тесты, которые я там делаю, показывают эквивалентность с версией, в которой просто пропущен $. Может кто-нибудь уточнить это для меня?

Ответы [ 3 ]

11 голосов
/ 12 января 2009

Здесь используется $, поскольку он имеет более низкий приоритет, чем обычное применение функции. Другой способ написать этот код выглядит так:

instance Monad [] where
   xs >>= f = (concat . map f) xs

Идея состоит в том, чтобы сначала создать функцию (concat . map f), а затем применить ее к аргументу (xs). Как показано, это также можно сделать, просто поместив круглые скобки вокруг первой части.

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

instance Monad [] where
  xs >>= f = concat . (map f xs)

Что не имеет смысла, потому что второй аргумент оператора композиции функций вообще не является функцией. Хотя следующее определение имеет смысл:

instance Monad [] where
  xs >>= f = concat (map f xs)

Кстати, это тоже определение, которое я бы предпочел, потому что мне кажется, что оно намного понятнее.

3 голосов
/ 12 января 2009

Я бы хотел объяснить, почему ИМХО это не используемый стиль:

instance Monad [] where
  xs >>= f = concat (map f xs)

concat . map f - пример так называемого письма в стиле pointfree; где pointfree означает «без точки применения». Помните, что в математике в выражении y=f(x) мы говорим, что f применяется к точке x. В большинстве случаев вы можете сделать последний шаг, заменив:

f x = something $ x

с

f = something

как f = concat . map f, и это на самом деле бессмысленный стиль. То, что яснее, является спорным, но стиль pointfree дает другую точку зрения, которая также полезна, поэтому иногда используется, даже когда она не совсем нужна.

РЕДАКТИРОВАТЬ: я заменил бессмысленно на pointfree и исправил некоторые примеры после комментария Alasdair, которого я должен поблагодарить.

1 голос
/ 22 января 2009

Причиной использования $ здесь является подпись типа (.):

(.) :: (b -> c) -> (a -> c) -> a -> c

Здесь мы имеем

map f :: [a] -> [[b]]

и

concat :: [[b]] -> [b]

Таким образом, мы получаем

concat . map f :: [a] -> [b]

и тип (.) Может быть записан как

(.) :: ([[b]] ​​-> [b]) -> ([a] -> [[b]]) -> [a] -> [b]

Если бы мы использовали concat . map f xs, мы бы увидели, что

map f xs :: [[b]]

И поэтому не может использоваться с (.). (тип должен быть (.) :: (a -> b) -> a -> b

...