Как я могу написать эту функцию в стиле point-free?
Я просто хочу сделать общие улучшения для моего кода Haskell и задавался вопросом, можно ли сделать следующую функцию без точек? В основном ради любопытства.
Учитывая две функции, которые мы хотели бы использовать в нашем filter
:
isZero = (==0)
isOne = (==1)
Как мы будем использовать эти две функции в нашем надуманном примере, но сделав его бесконтактным?
filter (\x -> isZero x || isOne x) [0..100]
Ответы
Ответ 1
Здесь онлайн-сервис для конвертирования кода Haskell
в бессточное.
Он предлагает: filter (liftM2 (||) isZero isOne) [0..100]
liftA2 (||) isZero isOne
или (||) <$> isZero <*> isOne
также возможно
(||) <$> isZero
имеет тип a0 -> Bool -> Bool
, а это композиция (||)
и isZero
. Эта композиция принимает число (для isZero
) и булево (как еще один аргумент для (||)
)
Итак, это то же самое, что \x y -> (||) (isZero x) y
Тип функции - это экземпляр Applicative Functor
, и мы можем посмотреть его реализацию:
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
Итак, (||) <$> isZero <*> isOne
совпадает с \x -> ((||) <$> isZero) x (isOne x)
и таким же, как \x -> (||) (isZero x) (isOne x)
Таким образом, если там z x = y (f x) (g x)
, то оно может быть преобразовано в свободное число: z = y <$> f <*> g
Ответ 2
Альтернативной свободной точкой будет использование моноида a -> Any
:
λ 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]
Это немного дольше, чем решение Applicative
, но я думаю, что немного легче следовать, когда у вас больше условий для объединения. Сравнить
getAny . (Any . isZero <> Any . isOne <> Any . isSquare <> Any . isPrime)
или
getAny . foldMap (Any .) [isZero, isOne, isSquare, isPrime]
и
liftA2 (||) (liftA2 (||) (liftA2 (||) isZero isOne) isSquare) isPrime
или
liftA2 (||) isZero $ liftA2 (||) isOne $ liftA2 (||) isSquare isPrime
Хотя, если честно, если бы у меня было много таких дел, у меня возникло бы желание определить <||> = liftA2 (||)
и сделать
isZero <||> isOne <||> isSquare <||> isPrime