Как понять это использование `$` в 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 [...]
$ 4 odd
: Это не будет работать, потому что операторы должны быть заключены в круглые скобки, если они не используются в инфиксной форме. Если бы вы должны были сделать($) 4 odd
, это не будет работать, потому что порядок аргументов неправильный, вы хотите4
быть вторым аргументом. Вы могли бы написать($) odd 4
хоть.($ 4) odd
: Это работает, потому что он использует разделы оператора, а здесь4
предоставляется в качестве второго аргумента$
, Это как(++ "world") "hello "
быть таким же, как"hello " ++ "world"
,Когда у тебя есть
($ 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 $)
, но это незначительная разница на практике.