online - haskell shampoo
¿Cómo escribiría esta función en un estilo sin puntos? (2)
Solo estoy buscando hacer mejoras generales en mi código Haskell, y me preguntaba si la siguiente función podría quedar libre de puntos. Sobre todo por el bien de la curiosidad.
Dadas dos funciones que nos gustaría usar en nuestro filter
:
isZero = (==0)
isOne = (==1)
¿Cómo podríamos utilizar esas dos funciones en nuestro ejemplo artificial, pero hacerlo sin puntos?
filter (/x -> isZero x || isOne x) [0..100]
Hay un online-service para convertir Haskell
código de Haskell
en un punto libre.
Se sugiere: filter (liftM2 (||) isZero isOne) [0..100]
liftA2 (||) isZero isOne
o (||) <$> isZero <*> isOne
también es posible
(||) <$> isZero
tiene el tipo a0 -> Bool -> Bool
y es la composición de (||)
e isZero
. Esta composición toma un número (para isZero
) y un booleano (como otro argumento para (||)
)
Entonces, es lo mismo que /xy -> (||) (isZero x) y
El tipo de función es una instancia de Applicative Functor
y podemos ver su implementación:
instance Applicative ((->) r) where
pure x = (/_ -> x)
f <*> g = /x -> f x (g x)
Entonces, (||) <$> isZero <*> isOne
es lo mismo que /x -> ((||) <$> isZero) x (isOne x)
y lo mismo que /x -> (||) (isZero x) (isOne x)
Por lo tanto, si hay zx = y (fx) (gx)
, se puede transformar en punto libre: z = y <$> f <*> g
Una forma libre de puntos alternativa sería utilizar a -> Any
mono a -> Any
monoide:
λ import Data.Monoid (Any(..))
λ :t getAny . (Any . isZero <> Any . isOne)
getAny . (Any . isZero <> Any . isOne)
:: (Num a, Eq a) => a -> Bool
λ filter (getAny . (Any . isZero <> Any . isOne)) [0..100]
[0,1]
Es un poco más largo que la solución Applicative
, pero creo que es un poco más fácil de seguir cuando tienes más condiciones para combinar. Comparar
getAny . (Any . isZero <> Any . isOne <> Any . isSquare <> Any . isPrime)
o
getAny . foldMap (Any .) [isZero, isOne, isSquare, isPrime]
y
liftA2 (||) (liftA2 (||) (liftA2 (||) isZero isOne) isSquare) isPrime
o
liftA2 (||) isZero $ liftA2 (||) isOne $ liftA2 (||) isSquare isPrime
Aunque para ser honesto, si tuviera que hacer muchas de estas cosas, me sentiría tentado a definir <||> = liftA2 (||)
y hacer
isZero <||> isOne <||> isSquare <||> isPrime