Trucco per “riutilizzo” argomenti in Haskell?
-
30-09-2019 - |
Domanda
Di tanto in tanto inciampo sul problema che voglio esprimere "si prega di utilizzare l'ultimo argomento due volte", per esempio al fine di scrivere stile pointfree o per evitare un lambda. Per es.
sqr x = x * x
potrebbe essere scritto come
sqr = doubleArgs (*) where
doubleArgs f x = f x x
O considerare questa funzione un po 'più complicato (tratto da questa domanda ):
ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)
Potrei scrivere questo codice pointfree se ci fosse una funzione come questa:
ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
dup f f1 f2 x = f (f1 x) (f2 x)
Ma come non riesco a trovare qualcosa di simile doubleArgs o dup in Hoogle, quindi credo che potrei mancare un trucco o idioma qui.
Soluzione
Da Control.Monad
:
join :: (Monad m) -> m (m a) -> m a
join m = m >>= id
instance Monad ((->) r) where
return = const
m >>= f = \x -> f (m x) x
Espansione:
join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
= \x -> id (f x) x
= \x -> f x x
Quindi, sì, Control.Monad.join
.
Oh, e per il vostro esempio pointfree, Hai provato a usare la notazione applicativa (da Control.Applicative
):
ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails
(anche io non so perché la gente è così appassionato di a ++ (x:b)
invece di a ++ [x] ++ b
... non più veloce è - l'inliner si prenderà cura di esso - e la seconda è molto più simmetrica Oh bene!)
Altri suggerimenti
Quello che tu chiami 'doubleArgs' è più spesso chiamato DUP - è il combinatore W (chiamato usignolo in Per Mock a Mockingbird) - "il duplicatore elementare".
Quello che tu chiami 'DUP' è in realtà il combinatore 'Starling-prime'.
Haskell ha un abbastanza piccolo "base combinatore" vedere Data.Function, più alcune operazioni applicative e monadiche aggiungere più combinatori "standard" in virtù delle istanze di funzione per applicativa e Monade (<*> dal applicativo è la S - storno combinatore per l'istanza funzionale, liftA2 & liftM2 sono Starling-prime). Non sembra essere molto entusiasmo nella comunità per l'espansione Data.Function, così mentre combinatori sono divertente, pragmaticamente sono venuto a preferire a lungo mano in situazioni in cui un combinatore non è direttamente disponibile.
Ecco un'altra soluzione per la seconda parte della mia domanda: Frecce
import Control.Arrow
ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))
Il &&&
( "fan-out") distribuisce un argomento per due funzioni e restituisce la coppia di risultati. >>>
( "e poi") inverte l'ordine di applicazione funzione, che permette di avere una catena di operazioni da sinistra a destra. second
funziona solo sulla seconda parte di una coppia. Naturalmente è necessario un uncurry
alla fine di alimentare la coppia in una funzione che si aspetta due argomenti.