2011-09-13 15 views
73

Guardando attraverso il Preludio Haskell, mi see a functionconst:Qual è il punto di "const" nel Preludio Haskell?

const x _ = x 

non posso sembrare trovare qualche cosa utili per quanto riguarda questa funzione.

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

+5

Un esempio: 'backgroundColor :: Text -> Color' è per me' backgroundColor = const White' – Zhen

risposta

68

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

x >> y = x >>= const y 

E 'un po' più ordinato rispetto all'utilizzo di un lambda

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

e si può anche usare punto-libera

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

anche se in questo caso non lo consiglio particolarmente.

+8

+1. Si presenta anche frequentemente quando si usano i combinatori di parser. –

+37

Ah, 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]' risulta in '[42, 42, 42, 42, 42]'. – stusmith

+2

stusmith: hai capito. 'const' è utile per applicare a un singolo argomento per ottenere una funzione in cui uno è necessario (come passare a' map'). – Conal

24

Per aggiungere alla risposta diretta eccellente di Hammar: funzioni umili come const e id sono davvero utili in funzione di ordine superiore per la stessa ragione che esse sono fondamentali nel SKI combinator calculus.

Non che io pensi che le funzioni di preludio di haskell siano state modellate in modo cosciente dopo quel sistema formale o altro. È solo che la creazione di astrazioni ricche in haskell è molto semplice, quindi spesso vedi questi tipi di cose teoriche emergere come praticamente utili.

senza vergogna, ma ho bloggato su come l'istanza applicativo per (->) sono in realtà i S e K combinatori here, se questo è il tipo di cosa siete in.

+7

Bene, i combinatori SKI hanno sicuramente influenzato il Preludio. Ricordo di aver discusso con Joe Fasel se il combinatore S doveva essere incluso o meno. – augustss

+4

Per inciso, '((->) e)' è anche il lettore monade - con 'Reader' e simili sono solo wrapper' newtype' - e la funzione 'ask' è quindi' id', quindi questo è il ' I' combinator pure. Se si guarda invece alla base originale BCKW di Haskell Curry, 'B',' K' e 'W' sono' fmap', 'return' e' join' rispettivamente. –

+1

Il link del blog nella risposta è morto. Dovrebbe ora puntare qui: http://brandon.si/code/do-applicative-functors-generalize-the-s-k-combinators/ – nsxt

19

Un semplice esempio per l'utilizzo di const è Data.Functor.(<$). Con questa funzione puoi dire: ho qui un funtore con qualcosa di noioso in esso, 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 scritta non come inutile:

cool <$ uncool = fmap (const cool) uncool 

Vedete come const è qui usato per "dimenticare" l'ingresso.

11

Un altro uso è implementare funzioni membro della classe che hanno un argomento fittizio che non deve essere valutato (utilizzato per risolvere tipi ambigui). Esempio che potrebbe essere in Data.bits:

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

Utilizzando const si dice esplicitamente che stiamo definendo valori costanti.

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

13

Non riesco a trovare nulla di rilevante riguardo questa funzione.

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

Per esempio, il seguente (inefficiente!) L'attuazione di length,

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

può essere riscritta come

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

che è forse più elegante.

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

+2

Mi ci sono voluti 5 minuti per capire come funziona :) –

1

const potrebbe essere proprio l'implementazione che stai cercando in combinazione con 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 come modo:

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

posso dare una definizione straight-forward con pattern matching:

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

Cosa succede se voglio una soluzione inutile (tacita) per questo tipo di riscritture? Alcuni pensano e giocherellano più tardi, la risposta è che possiamo esprimere qualsiasi riscrittura con (&&&), const, (.), fst, snd. Si noti che (&&&) è da Control.Arrow.

La soluzione dell'esempio utilizzando queste funzioni è:

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

Nota la somiglianza con (a,(c,(5,a))). Cosa succede se sostituiamo &&& con ,? Poi si legge:

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

Notate come a è il primo elemento del primo elemento, e questo è ciò che fst.fst progetti. Nota come c è il primo elemento del secondo elemento, ed è quello che i progetti fst.snd. Cioè, le variabili diventano il percorso verso la loro fonte.

const ci consente 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, come ad esempio maybe, either, bool). Anche in questo caso, const svolge il ruolo di introduzione di costanti. Puoi vedere questo lavoro nel pacchetto Data.Function.Tacit.

Quando si inizia in modo astratto, all'obiettivo, e quindi si lavora verso un'implementazione, si può essere sorpresi dalle risposte. Vale a dire, qualsiasi funzione può essere misteriosa come qualsiasi ingranaggio in una macchina. Se si tira indietro per vedere l'intera macchina in vista, tuttavia, è possibile comprendere il contesto in cui tale ingranaggio è necessario.