Qual è il punto di "const" nel preludio di Haskell?


92

Guardando l'Haskell Prelude, vedo una funzione const :

const x _ = x

Non riesco a trovare nulla di rilevante per quanto riguarda questa funzione.

Qual e il punto? Qualcuno può fornire un esempio di dove potrebbe essere utilizzata questa funzione?


10
Un esempio: backgroundColor :: Text -> Colorè per mebackgroundColor = const White
Zhen

Risposte:


83

È utile per passare a funzioni di ordine superiore quando non è necessaria tutta la loro flessibilità. Ad esempio, l'operatore di sequenza monadico >>può essere definito in termini di operatore di legame monadico come

x >> y = x >>= const y

È un po 'più ordinato che usare un lambda

x >> y = x >>= \_ -> y

e puoi persino usarlo senza punti

(>>) = (. const) . (>>=)

anche se in questo caso non lo consiglio particolarmente.


9
+1. Viene anche visualizzato frequentemente quando si utilizzano combinatori parser.
Fred Foo

47
Ahh, quindi è più un "generatore di funzioni": lo uso con un argomento e mi dà una funzione (prendendo un argomento) che restituisce sempre un valore costante. Quindi map (const 42) [1..5]risultati [42, 42, 42, 42, 42].
stusmith

2
stusmith: Hai capito. constè utile per l'applicazione a un singolo argomento per produrre una funzione in cui è necessaria (come il passaggio a map).
Conal

8
@stusmith: Puoi usarlo in alcuni modi interessanti:head = foldr const (error "Prelude.head: empty list")
rampion

27

Da aggiungere all'eccellente risposta diretta di Hammar: funzioni umili come conste idsono davvero utili come funzione di ordine superiore per lo stesso motivo per cui sono fondamentali nel calcolo del combinatore SKI .

Non che io pensi che le funzioni di preludio di haskell siano state modellate consapevolmente su quel sistema formale o altro. È solo che creare ricche astrazioni in haskell è molto facile, quindi spesso vedi questo tipo di cose teoriche emergere come praticamente utili.

Plug senza vergogna, ma ho scritto sul blog di come l'istanza applicativa per (->)siano effettivamente i combinatori Se qui , se questo è il genere di cose che ti interessano.K


8
Ebbene, i combinatori di sci hanno sicuramente influenzato il Preludio. Ricordo di aver discusso con Joe Fasel se il combinatore S dovesse essere incluso o meno.
agosto

4
Per inciso, ((->) e)è anche la monade del lettore - con Readere simili sono solo newtypewrapper - e la askfunzione è quindi id, quindi è anche il Icombinatore. Se si guarda invece alla base BCKW originale di Haskell Curry, B, K, e Wsono fmap, returne join, rispettivamente.
CA McCann

1
Il link al blog nella risposta è morto. Ora dovrebbe puntare qui: brandon.si/code/…
nsxt

22

Un semplice esempio per l'utilizzo constè Data.Functor.(<$). Con questa funzione puoi dire: ho qui un funtore con qualcosa di noioso dentro, ma invece voglio avere quell'altra cosa interessante in esso, senza cambiare la forma del funtore. Per esempio

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

La definizione è:

(<$) :: a -> f b -> f a
(<$) =  fmap . const

o scritto non come inutile:

cool <$ uncool =  fmap (const cool) uncool

Vedete come constviene utilizzato qui per "dimenticare" l'input.


21

Non riesco a trovare nulla di rilevante per quanto riguarda questa funzione.

Molte delle altre risposte discutono applicazioni relativamente esoteriche (almeno per il nuovo arrivato) di const. Eccone uno semplice: puoi usare constper sbarazzarti di un lambda che prende due argomenti, butta via il primo ma fa qualcosa di interessante con il secondo.

Ad esempio, la seguente (inefficiente ma istruttiva) implementazione di length,

length' = foldr (\_ acc -> 1 + acc) 0

può essere riscritto come

length' = foldr (const (1+)) 0

che è forse più elegante.

L'espressione const (1+)è in effetti semanticamente equivalente a \_ acc -> 1 + acc, perché prende un argomento, lo getta via e restituisce la sezione (1+) .


4
Mi ci sono voluti 5 minuti per capire come funziona :)
Mukesh Soni

15

Un altro utilizzo consiste nell'implementare funzioni membro della classe che hanno un argomento fittizio che non dovrebbe essere valutato (usato per risolvere tipi ambigui). Esempio che potrebbe essere in Data.bits:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

Usando const diciamo esplicitamente che stiamo definendo valori costanti.

Personalmente non mi piace l'uso di parametri fittizi, ma se vengono usati in una classe, questo è un modo piuttosto carino di scrivere istanze.


Gli argomenti proxy sono davvero molto migliori e quando si prendono di mira GHC recenti, le applicazioni di tipo fanno il trucco in modo ordinato.
dfeuer

2

constpotrebbe essere solo l'implementazione che stai cercando insieme ad altre funzioni. Ecco un esempio che ho scoperto.

Supponiamo di voler riscrivere una struttura di 2-tuple in un'altra struttura di 2-tuple. Potrei esprimere questo in questo modo:

((a,b),(c,d))  (a,(c,(5,a)))

Posso dare una definizione semplice con il pattern matching:

f ((a,b),(c,d)) = (a,(c,(5,a)))

E se volessi una soluzione inutile (tacita) per questo tipo di riscritture? Pensando e giocherellando dopo, la risposta è che possiamo esprimere qualsiasi riscrittura con (&&&), const, (.), fst, snd. Nota che (&&&)è da Control.Arrow.

La soluzione dell'esempio che utilizza queste funzioni è:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

Nota la somiglianza con (a,(c,(5,a))). E se sostituiamo &&&con ,? Quindi si legge:

(fst.fst, (fst.snd, (const 5, fst.fst)))

Nota come aè il primo elemento del primo elemento, e questo è ciò che fst.fstprogetti. Nota come cè il primo elemento del secondo elemento, e questo è ciò che fst.sndprogetti. Cioè, le variabili diventano il percorso verso la loro fonte.

constci permette di introdurre costanti. Interessante come il nome si allinea con il significato!

Ho poi generalizzato questa idea con applicativo in modo che si può scrivere qualsiasi funzione in uno stile inutile (finché si dispone di un'analisi caso disponibili come funzioni, ad esempio maybe, either, bool). Ancora una volta, constsvolge il ruolo di introdurre costanti. Puoi vedere questo lavoro nel pacchetto Data.Function.Tacit .

Quando inizi in modo astratto, all'obiettivo, e poi lavori verso un'implementazione, puoi essere sorpreso dalle risposte. Vale a dire, qualsiasi funzione può essere misteriosa come qualsiasi ingranaggio di una macchina. Tuttavia, se ti tiri indietro per visualizzare l'intera macchina, puoi capire il contesto in cui è necessario quell'ingranaggio.


2

Supponi di voler creare un elenco di Nothingsuguale alla lunghezza di una stringa. Poiché constrestituisce il suo primo argomento, indipendentemente dal secondo, puoi fare:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

o, più esplicitamente:

listOfNothing st = map (const Nothing) st

0

Supponi di voler ruotare un elenco. Questo è un modo idiomatico per farlo in Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

Questa funzione comprime due array con la funzione const, il primo è un array ciclico infinito, il secondo è l'array con cui hai iniziato.

const funge da controllo dei limiti e utilizza l'array originale per terminare l'array ciclico.

Vedi: Ruota un elenco in Haskell


0

Non riesco a trovare nulla di rilevante per quanto riguarda questa funzione.

Supponiamo di voler generare tutte le sottosequenze di un dato elenco.

Per ogni elemento della lista, in un dato punto è possibile scegliere tra True (includerlo nella sottosequenza corrente) o False (non includerlo). Questo può essere fatto usando la funzione filterM .

Come questo:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

Ad esempio, vogliamo tutte le sottosequenze di [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],[]]
 λ> 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.