Подтвердить что ты не робот

Как я могу написать эту функцию в стиле point-free?

Я просто хочу сделать общие улучшения для моего кода Haskell и задавался вопросом, можно ли сделать следующую функцию без точек? В основном ради любопытства.

Учитывая две функции, которые мы хотели бы использовать в нашем filter:

isZero = (==0)
isOne = (==1)

Как мы будем использовать эти две функции в нашем надуманном примере, но сделав его бесконтактным?

filter (\x -> isZero x || isOne x) [0..100]
4b9b3361

Ответ 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