Olhando o Prelúdio de Haskell, vejo uma função const :
const x _ = x
Não consigo encontrar nada relevante sobre esta função.
Qual é o ponto? Alguém pode dar um exemplo de onde essa função pode ser usada?
Olhando o Prelúdio de Haskell, vejo uma função const :
const x _ = x
Não consigo encontrar nada relevante sobre esta função.
Qual é o ponto? Alguém pode dar um exemplo de onde essa função pode ser usada?
Respostas:
É útil para passar para funções de ordem superior quando você não precisa de toda a flexibilidade. Por exemplo, o operador de sequência monádica >>pode ser definido em termos do operador de ligação monádica como
x >> y = x >>= const y
É um pouco mais limpo do que usar um lambda
x >> y = x >>= \_ -> y
e você pode até mesmo usá-lo sem pontos
(>>) = (. const) . (>>=)
embora eu particularmente não recomende isso neste caso.
map (const 42) [1..5]resulta em [42, 42, 42, 42, 42].
consté útil para aplicar a um único argumento para produzir uma função onde uma é necessária (como passar para map).
head = foldr const (error "Prelude.head: empty list")
Para adicionar à excelente resposta direta de Hammar: funções humildes gostam conste idsão realmente úteis como uma função de ordem superior pela mesma razão que são fundamentais no cálculo combinador de SKI .
Não que eu ache que as funções do prelúdio de haskell foram modeladas conscientemente a partir desse sistema formal ou algo assim. É que criar abstrações ricas em haskell é muito fácil, então você frequentemente vê esses tipos de coisas teóricas emergirem como úteis na prática.
Plugue desavergonhado, mas escrevi sobre como a instância Applicative for (->)é realmente os combinadores Se aqui , se esse é o tipo de coisa que você gosta.K
((->) e)também é a mônada do leitor - com Readere semelhantes sendo apenas newtypeinvólucros - e a askfunção é então id, então esse é o Icombinador também. Se você olhar em vez de base BCKW original de Haskell Curry, B, K, e Wsão fmap, returne, joinrespectivamente.
Um exemplo simples de uso consté Data.Functor.(<$). Com esta função você pode dizer: eu tenho aqui um functor com algo chato nele, mas ao invés disso eu quero ter aquela outra coisa interessante nele, sem mudar o formato do functor. Por exemplo
import Data.Functor
42 <$ Just "boring"
--> Just 42
42 <$ Nothing
--> Nothing
"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]
A definição é:
(<$) :: a -> f b -> f a
(<$) = fmap . const
ou escrito não tão sem sentido:
cool <$ uncool = fmap (const cool) uncool
Você vê como consté usado aqui para "esquecer" a entrada.
Não consigo encontrar nada relevante sobre esta função.
Muitas das outras respostas discutem aplicações relativamente esotéricas (pelo menos para o recém-chegado) de const. Aqui está um simples: você pode usar constpara se livrar de um lambda que leva dois argumentos, joga fora o primeiro, mas faz algo interessante com o segundo.
Por exemplo, a seguinte implementação (ineficiente, mas instrutiva) de length,
length' = foldr (\_ acc -> 1 + acc) 0
pode ser reescrito como
length' = foldr (const (1+)) 0
o que talvez seja mais elegante.
A expressão const (1+)é na verdade semanticamente equivalente a \_ acc -> 1 + acc, porque pega um argumento, joga-o fora e retorna a seção (1+) .
Outro uso é implementar funções de membro de classe que possuem um argumento fictício que não deve ser avaliado (usado para resolver tipos ambíguos). Exemplo que poderia estar em Data.bits:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
Ao usar const, dizemos explicitamente que estamos definindo valores constantes.
Pessoalmente, não gosto do uso de parâmetros fictícios, mas se eles forem usados em uma classe, essa é uma maneira bastante boa de escrever instâncias.
constpode ser apenas a implementação que você está procurando em conjunto com outras funções. Aqui está um exemplo que descobri.
Digamos que queremos reescrever uma estrutura de 2 tuplas em outra estrutura de 2 tuplas. Posso expressar isso assim:
((a,b),(c,d)) ⇒ (a,(c,(5,a)))
Posso dar uma definição direta com correspondência de padrões:
f ((a,b),(c,d)) = (a,(c,(5,a)))
E se eu quiser uma solução inútil (tácita) para esse tipo de reescrita? Pensando e brincando depois, a resposta é que podemos expressar qualquer reescrita com (&&&), const, (.), fst, snd. Observe que (&&&)é de Control.Arrow.
A solução do exemplo usando essas funções é:
(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))
Observe a semelhança com (a,(c,(5,a))). E se substituirmos &&&por ,? Em seguida, lê-se:
(fst.fst, (fst.snd, (const 5, fst.fst)))
Observe como aé o primeiro elemento do primeiro elemento e é isso que fst.fstprojeta. Observe como cé o primeiro elemento do segundo elemento, e é isso que fst.sndprojeta. Ou seja, as variáveis se tornam o caminho para sua origem.
constnos permite introduzir constantes. Interessante como o nome se alinha com o significado!
Eu, então, generalizada esta ideia com Applicative de modo que você pode escrever qualquer função num estilo inútil (contanto que você tem análise de caso disponível como funções, tais como maybe, either, bool). Novamente, constdesempenha o papel de introduzir constantes. Você pode ver este trabalho no pacote Data.Function.Tacit .
Quando você começa abstratamente, no objetivo, e depois trabalha para uma implementação, pode se surpreender com as respostas. Ou seja, qualquer função pode ser tão misteriosa quanto qualquer engrenagem de uma máquina. Se você recuar para visualizar toda a máquina, poderá entender o contexto no qual essa engrenagem é necessária.
Digamos que você queira criar uma lista Nothingsigual ao comprimento de uma string. Conforme constretorna seu primeiro argumento, não importa o segundo, você pode fazer:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
ou, mais explicitamente:
listOfNothing st = map (const Nothing) st
Digamos que você queira girar uma lista. Esta é uma maneira idiomática de fazer isso em Haskell:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
Esta função compacta dois arrays com a função const, o primeiro sendo um array cíclico infinito, o segundo sendo o array com o qual você começou.
const atua como a verificação de limites e usa a matriz original para encerrar a matriz cíclica.
Não consigo encontrar nada relevante sobre esta função.
Suponha que você gostaria de gerar todas as subsequências de uma determinada lista.
Para cada elemento da lista, em um determinado ponto, você tem uma escolha de Verdadeiro (inclua-o na subsequência atual) ou Falso (não inclua). Isso pode ser feito usando a função filterM .
Como isso:
λ> import Control.Monad
λ> :t filterM
filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
λ>
Por exemplo, queremos todas as subsequências de [1..4] .
λ> filterM (const [True, False]) [1..4]
[[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
λ>
backgroundColor :: Text -> Coloris for mebackgroundColor = const White