Pergunta

Qual é a diferença entre o (.) ponto eo ($) cifrão?

Pelo que entendi, ambos são açúcar sintático para não precisar parênteses uso.

Foi útil?

Solução

O operador $ é para evitar parênteses. Qualquer coisa que aparece depois que ele terá precedência sobre qualquer coisa que vem antes.

Por exemplo, digamos que você tem uma linha que lê:

putStrLn (show (1 + 1))

Se você quiser se livrar desses parênteses, qualquer uma das seguintes linhas também faria a mesma coisa:

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

O objetivo principal do operador . não é parênteses evitar, mas para funções da cadeia. Ele permite que você amarrar a saída de qualquer aparece à direita para a entrada de qualquer aparece no lado esquerdo. Isso normalmente também resulta em menos parênteses, mas funciona de forma diferente.

Voltando ao mesmo exemplo:

putStrLn (show (1 + 1))
  1. (1 + 1) não tem uma entrada e, portanto, não pode ser usado com o operador ..
  2. show pode tomar um Int e retornar um String.
  3. putStrLn pode tomar um String e retornar um IO ().

Você pode show cadeia para putStrLn assim:

(putStrLn . show) (1 + 1)

Se isso é demasiado muitos parênteses para o seu gosto, se livrar deles com o operador $:

putStrLn . show $ 1 + 1

Outras dicas

Eles têm diferentes tipos e diferentes definições:

infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x

($) se destina a substituir a aplicação de função normal, mas em uma precedência diferente para parênteses ajudam a evitar. (.) é para compor duas funções em conjunto para fazer uma nova função.

Em alguns casos, eles são intercambiáveis, mas isso não é verdade em geral. O exemplo típico onde eles estão é:

f $ g $ h $ x

==>

f . g . h $ x

Em outras palavras, em uma cadeia de $s, todos, mas o último pode ser substituído por .

Observe também que ($) é a função identidade especializada para funcionar tipos . A função identidade esta aparência:

id :: a -> a
id x = x

Enquanto ($) esta aparência:

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

Note que eu adicionei intencionalmente parênteses adicionais na assinatura de tipo.

Utilizações de ($) geralmente pode ser eliminado através da adição de parênteses (a menos que o operador é utilizado em uma secção). Ex .: f $ g x se torna f (g x).

Usos de (.) são muitas vezes um pouco mais difícil de substituir; eles geralmente precisam de um lambda ou a introdução de um parâmetro de função explícita. Por exemplo:

f = g . h

se torna

f x = (g . h) x

se torna

f x = g (h x)

Espero que isso ajude!

($) permite funções a serem encadeadas sem adicionar parênteses para ordem de avaliação de controle:

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'

O (.) operador compor cria uma nova função sem especificar os argumentos:

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'

O exemplo acima é sem dúvida ilustrativa, mas não realmente mostrar a conveniência de usar composição. Aqui está outra analogia:

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"

Se nós usamos somente terceira vez, podemos evitar nomeá-lo usando um lambda:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

Finalmente, a composição nos permite evitar o lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

A versão curta e doce:

  • ($) chama a função que é seu argumento do lado esquerdo sobre o valor que é seu argumento do lado direito.
  • (.) compõe a função que é seu argumento do lado esquerdo na função que é seu argumento do lado direito.

Uma aplicação que é útil e me levou algum tempo para descobrir a partir da breve descrição em aprender uma haskell : Desde:

f $ x = f x

e parenthesizing o lado direito de uma expressão contendo um infixas convertidos operador de TI a uma função de prefixo, um pode escrever ($ 3) (4+) análogo ao (++", world") "hello".

Por que alguém faria isso? Para listas de funções, por exemplo. Ambos:

map (++", world") ["hello","goodbye"]`

e

map ($ 3) [(4+),(3*)]

são mais curtos do que map (\x -> x ++ ", world") ... ou map (\f -> f 3) .... Obviamente, as últimas variantes seria mais legível para a maioria das pessoas.

... ou você poderia evitar as construções . e $ usando pipelining :

third xs = xs |> tail |> tail |> head

Isso é depois que você adicionou na função auxiliar:

(|>) x y = y x

Uma ótima maneira de aprender mais sobre qualquer coisa (qualquer função) é lembrar que tudo é uma função! Esse mantra geral ajuda, mas em casos específicos, como operadores, que ajuda a lembrar este pequeno truque:

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

e

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

Apenas lembre-se de usar :t liberalmente, e envolver seus operadores em ()!

A minha regra é simples (eu sou novato também):

  • Não uso . se você quiser passar o parâmetro (chamar a função), e
  • Não uso $ se não houver nenhum parâmetro ainda (compor uma função)

Ou seja

show $ head [1, 2]

mas nunca:

show . head [1, 2]

Haskell: diferença entre . (ponto) e $ (cifrão)

Qual é a diferença entre o (.) ponto eo ($) cifrão ?. Pelo que entendi, ambos são açúcar sintático para não precisar parênteses uso.

Eles são não açúcar sintático para não precisar usar parênteses - são funções, - infixada, assim podemos chamá-los de operadores.

Compose, (.), e quando usá-lo.

(.) é a função de composição. Então

result = (f . g) x

é o mesmo que construir uma função que passa o resultado do seu argumento passado para g para f.

h = \x -> f (g x)
result = h x

Use (.) quando você não tem os argumentos disponíveis para passar para as funções que deseja compor.

Right associativa aplicar, ($), e quando usá-lo

($) é um associativos à direita aplicar a função com baixa precedência vinculativo. Então ele apenas calcula as coisas à direita do primeiro. Assim,

result = f $ g x

é o mesmo que este, processualmente (que assuntos desde Haskell é avaliada preguiçosamente, ele vai começar a avaliar f primeiro):

h = f
g_x = g x
result = h g_x

ou mais concisa:

result = f (g x)

Use ($) quando você tem todas as variáveis ??para avaliar antes de aplicar a função anterior para o resultado.

Podemos ver isso lendo a fonte para cada função.

Leia a fonte

Aqui está o fonte para (.):

-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

E aqui está a href="http://hackage.haskell.org/package/base-4.10.0.0/docs/src/GHC.Base.html#%24" rel="nofollow noreferrer"> fonte para ($):

-- | Application operator.  This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- >     f $ g $ h x  =  f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

Conclusão

Use composição quando você não precisa imediatamente avaliar a função. Talvez você quer passar a função que resulta da composição para outra função.

Use aplicação quando estamos fornecendo todos os argumentos para avaliação completa.

Assim, para nosso exemplo, seria semanticamente preferível fazer

f $ g x

quando temos x (ou melhor, os argumentos de g), e fazer:

f . g

quando não o fazemos.

Eu acho que um pequeno exemplo de onde você usaria . e não $ ajudaria a esclarecer as coisas.

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

Note que times6 é uma função que é criada a partir de composição de função.

Todas as outras respostas são muito bons. Mas há um detalhe importante usabilidade sobre como ghc trata $, que o verificador de tipos ghc permite instatiarion com / tipos quantificados maior pontuação. Se você olhar para o tipo de $ id por exemplo, você vai encontrá-lo vai ter uma função cujo argumento é em si uma função polimórfica. Pequenas coisas como essa não são dadas a mesma flexibilidade com um operador chateado equivalente. (Isso realmente me faz pensar se $! Merece o mesmo tratamento ou não)

scroll top