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