Truco para “reutilización” argumentos en Haskell?
-
30-09-2019 - |
Pregunta
De vez en cuando me tropiezo con el problema que quiero expresar "por favor utilice el último argumento dos veces", por ejemplo, con el fin de escribir estilo pointfree o para evitar una lambda. Por ejemplo.
sqr x = x * x
podría escribir como
sqr = doubleArgs (*) where
doubleArgs f x = f x x
O considere esta función un poco más complicado (tomado de esta pregunta ):
ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)
Podría escribir el código pointfree si había una función como esta:
ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
dup f f1 f2 x = f (f1 x) (f2 x)
Pero como no puedo encontrar algo como doubleArgs o dup en Hoogle, así que supongo que puede ser que se pierda un truco o idioma aquí.
Solución
De 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
Ampliación:
join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
= \x -> id (f x) x
= \x -> f x x
Así que, sí, Control.Monad.join
.
Ah, y por su ejemplo pointfree, ¿Ha intentado utilizar la notación aplicativo (de Control.Applicative
):
ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails
(también no sé por qué la gente es tan aficionado a a ++ (x:b)
en lugar de a ++ [x] ++ b
... no es más rápido - el revestimiento interior se hará cargo de él - y este último es mucho más simétrica Oh bien!)
Otros consejos
Lo que llamas '' doubleArgs se llama con más frecuencia DUP - es el combinador W (llamada curruca en burlarse de un ruiseñor) - "el duplicador primaria".
Lo que llamas 'dup' es en realidad el combinador 'Starling-prime'.
Haskell tiene una bastante pequeña "base combinador" ver Data.Function, además de algunas operaciones de aplicativos y Monádicos añadir más combinadores "estándar" en virtud de las instancias de función para Aplicativo y Mónada (<*> del Aplicativo es el S - Starling combinador para la instancia funcional, liftA2 y liftM2 se Starling-prime). No parece ser mucho entusiasmo en la comunidad para expandir Data.Function, así que mientras combinadores son muy entretenidos, pragmáticamente he llegado a preferir siempre a mano en situaciones en las que un combinador no está disponible directamente.
Aquí hay otra solución para la segunda parte de mi pregunta: Flechas
import Control.Arrow
ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))
El &&&
( "fanout") distribuye un argumento a dos funciones y devuelve el par de los resultados. >>>
( "y luego") invierte el orden de aplicación de función, lo que permite tener una cadena de operaciones de izquierda a derecha. second
sólo funciona en la segunda parte de un par. Por supuesto que necesita un uncurry
al final de alimentar a la pareja en una función que espera dos argumentos.