const
potrebbe 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.fst
progetti. Nota come c
è il primo elemento del secondo elemento, e questo è ciò che fst.snd
progetti. Cioè, le variabili diventano il percorso verso la loro fonte.
const
ci 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, const
svolge 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.
backgroundColor :: Text -> Color
è per mebackgroundColor = const White