Что на самом деле делает $ function в haskell?
Я знаю
$ :: (a->b) -> a -> b
f $ x = f x
Интуитивно, мне кажется, как бы сказать: 1. $ задерживает оценку функции слева 2. оценивает то, что справа 3. передает результат слева направо.
И это имеет смысл для меня, когда,
ghci> length $ [1..5]
5
ghci> ($) length [1..5]
5
То, что я не понимаю, это почему,
ghci> ($ [1..5]) length
5
Судя по типу $, разве этот (первый) аргумент не должен быть функцией?
3 ответа
Это связано с разбором. В Haskell вы можете написать (op arg)
где op
является инфиксным оператором Это не то же самое, что ((op) arg)
, И ты можешь написать (arg op)
также! Например:
GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help
Prelude> :t (+ 4)
(+ 4) :: Num a => a -> a
Prelude> :t (4 +)
(4 +) :: Num a => a -> a
То есть, (+ 4)
это функция \x -> x + 4
а также (4 +)
это функция \y -> 4 + y
, В случае сложения это равные функции, но это сейчас не очень важно.
Теперь давайте попробуем тот же трюк на $
:
Prelude> :t ($ [1,2,3,4])
($ [1,2,3,4]) :: Num t => ([t] -> b) -> b
Теперь сюрприз, мы получили \f -> f $ [1,2,3,4]
, Мы также можем написать
Prelude> :t (length $)
(length $) :: [a] -> Int
чтобы получить функцию \l -> length $ l
, Но как насчет этого:
Prelude> :t ($ length)
($ length) :: (([a] -> Int) -> b) -> b
Это странно, но имеет смысл! У нас есть \f -> f $ length
функционал, который ожидает получить функцию f
типа ([a] -> Int) -> b)
это будет применяться к length
, Существует четвертая возможность:
Prelude> :t ([1,2,3,4] $)
<interactive>:1:2:
Couldn't match expected type `a0 -> b0' with actual type `[t0]'
In the first argument of `($)', namely `[1, 2, 3, 4]'
In the expression: ([1, 2, 3, 4] $)
Все так и должно быть, потому что [1,2,3,4]
это не функция. Что если мы напишем $
в скобках? Тогда его особое значение как инфиксного оператора исчезает:
Prelude> :t (($) length)
(($) length) :: [a] -> Int
Prelude> :t (($) [1,2,3,4])
<interactive>:1:6:
Couldn't match expected type `a0 -> b0' with actual type `[t0]'
In the first argument of `($)', namely `[1, 2, 3, 4]'
In the expression: (($) [1, 2, 3, 4])
Prelude> :t (length ($))
<interactive>:1:9:
Couldn't match expected type `[a0]'
with actual type `(a1 -> b0) -> a1 -> b0'
In the first argument of `length', namely `($)'
In the expression: (length ($))
Prelude> :t ([1,2,3,4] ($))
<interactive>:1:2:
The function `[1, 2, 3, 4]' is applied to one argument,
but its type `[t0]' has none
In the expression: ([1, 2, 3, 4] ($))
Итак, чтобы ответить на ваш вопрос: $ [1,2,3,4]
анализируется как \f -> f $ [1,2,3,4]
так что имеет смысл применить его к length
, тем не мение ($) [1, 2, 3, 4]
не имеет большого смысла, потому что ($)
не рассматривается как инфиксный оператор.
Кстати, $
"ничего не делает", так сказать. Он в основном используется для более удобочитаемого ввода, потому что он имеет низкий приоритет и поэтому мы можем написать f $ g $ h $ x
вместо f (g (h x))
,
Ваш вопрос действительно о том, что называется разделами оператора. С любым оператором в Haskell (я буду использовать +
в качестве примера) можно написать что-то вроде (+ arg)
или же (arg +)
, Это просто сокращенный синтаксис для анонимных функций (\x -> x + arg)
а также (\x -> arg + x)
соответственно.
Итак ($ [1..5])
синтаксис просто означает (\x -> x $ [1..5])
который так же, как (\x -> x [1..5])
(т.е. функция, которая проходит [1..5]
функции, переданной в качестве аргумента).
($ [1..5])
это раздел. Это частично применяемый оператор. Это сокращение для (\f -> f $ [1..5])
,
Разделы позволяют вам передать один аргумент бинарному оператору и создать функцию - функцию, которая ожидает оставшийся аргумент.
Взгляните на http://www.haskell.org/haskellwiki/Section_of_an_infix_operator