Как понять это использование `$` в Haskell

Это происходит в ситуации, когда вы хотите применить несколько функций к одной и той же переменной, это может выглядеть так:

map (\f->f 4) [odd, even]

но из Ляха $ сделать это очень аккуратно

map ($ 4) [odd, even]

почему это работает. сначала я набираю его в ghci, как $ 4 oddне получилось, тогда я наберу ($ 4) odd, который работает отлично. тогда я проверяю тип ($ 4) с помощью :t который показывает ($ 4) :: Num a => (a -> b) -> b, odd является odd :: Integral a => a -> Bool, Кажется, имеет смысл, но все еще не ясно для меня.

Может кто-нибудь объяснить это ясно, это еще одно распространенное использование $и есть ли другие более широкое использование $,

3 ответа

Анатомия оператора

$ Приложение оператора имеет вид:

($) :: (a -> b) -> a -> b

Это часто встречается в ситуации, когда вы хотите избежать заключительной пары круглых скобок:

func a (b + c)

равно:

func a $ b + c

Волшебство этого просто объясняется в декларации исправления:

infixr 0

Это значит: все, что после $ будут сгруппированы в одну сущность, как если бы они были заключены в круглые скобки.

Конечно, это также может быть "вложенным" так:

func a $ b + other $ c - d

что значит:

func a (b + other (c - d))

Оператор приложения как функция

Ваш случай очень интересен и, по моему опыту, используется не очень часто.

Давайте проанализируем это:

map ($ 4) [odd, even]

Мы знаем это mapТип:

map :: (a -> b) -> [a] -> [b]

Поведение, если кто-то забыл, это: принять первый аргумент (функция из a в b) и применить его к каждому a во втором списке аргументов, наконец, верните полученный список.

Ты можешь видеть ($ 4) как "передать 4 в качестве аргумента к чему-то". Который означает, что:

($ 4) func

такой же как:

func $ 4

Так:

map ($ 4) [odd, even]

средства:

[($ 4) odd, ($ 4) even]
[(odd $ 4), (even $ 4)]
[False, True]

Почему (func $) не нужно

Вы можете утверждать, что так же, как вы можете сделать (/ 4) а также (2 /) что соответственно означает "разделить что-то на 4" и "разделить 2 на что-то", вы могли бы сделать ($ 4) а также (func $) и ты был бы прав.

По факту:

(func $) 4

такой же как:

func $ 4
func 4

что так же, как:

($ 4) func

Но реальность такова:

map (func $) [...]

было бы ненужным, так как первый аргумент map всегда применяется к каждому аргументу в списке, делая вышеуказанное таким же, как:

map func [...]
  1. $ 4 odd: Это не будет работать, потому что операторы должны быть заключены в круглые скобки, если они не используются в инфиксной форме. Если бы вы должны были сделать ($) 4 odd, это не будет работать, потому что порядок аргументов неправильный, вы хотите 4 быть вторым аргументом. Вы могли бы написать ($) odd 4 хоть.

  2. ($ 4) odd: Это работает, потому что он использует разделы оператора, а здесь 4 предоставляется в качестве второго аргумента $, Это как (++ "world") "hello " быть таким же, как "hello " ++ "world",

  3. Когда у тебя есть ($ 4) :: Num a => (a -> b) -> b, а также odd :: Integral a => a -> BoolВам просто нужно выстроить типы. Так как каждый Integral a также Num aмы можем просто "обновить" (ограничить) Num в Integral чтобы это работало:

($ 4) :: Integral a => (a ->    b) -> b
odd   :: Integral a =>  a -> Bool

Так a ~ a а также b ~ Boolтак что вы можете сказать, что

($ 4) :: Integral a => (a -> Bool) -> Bool

Таким образом, применяя его к odd дает нам

($ 4) odd :: Bool

Это потому что ($ 4) odd такой же как odd $ 4, Глядя на определение $:

f $ x = f x

Мы можем сказать, что

odd $ 4 = odd 4

Который оценивает False,

Инфиксные операторы, такие как *, ++, или же $ обычно принимают два аргумента, как в

x ++ y

Если один аргумент отсутствует и они заключены в скобки, они вместо этого образуют раздел:

(x ++)
(++ y)

Эти разделы эквивалентны, соответственно,

\y -> x ++ y
\x -> x ++ y

то есть они обозначают функцию, которая отображает "отсутствующий аргумент" на результат. Например,

map ("A"++) ["a","b"] == [ "Aa","Ab" ]
map (++"A") ["a","b"] == [ "aA","bA" ]

оператор $ не является особенным в этом отношении. У нас есть

(f $)
($ x)

который обозначает

\x -> f $ x
\f -> f $ x

Первое не очень полезно, так как (f $) является \x -> f $ x что (это-) эквивалентно просто f (*). Второй вместо этого полезен.

(*) Быть разборчивым, seq можно различить undefined а также (undefined $), но это незначительная разница на практике.

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