简体   繁体   中英

How would I write this function in point-free style?

I'm just looking to make general improvements to my Haskell code, and was wondering if the following function could be made point-free? Mostly for curiosity's sake.

Given two functions which we'd like to use in our filter :

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

How would we go about utilising those two functions in our contrived example, but making it point-free?

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

There's a online-service for converting Haskell code to point-free.

It suggests: filter (liftM2 (||) isZero isOne) [0..100]

liftA2 (||) isZero isOne or (||) <$> isZero <*> isOne is also possible

(||) <$> isZero has type a0 -> Bool -> Bool and it's the composition of (||) and isZero . This composition takes a number (for isZero ) and a boolean (as another argument for (||) )

So, it's the same as \\xy -> (||) (isZero x) y

The function type is an instance of Applicative Functor and we can look at its implementation:

instance Applicative ((->) r) where  
    pure x = (\_ -> x)  
    f <*> g = \x -> f x (g x)

So, (||) <$> isZero <*> isOne is the same as \\x -> ((||) <$> isZero) x (isOne x) and the same as \\x -> (||) (isZero x) (isOne x)

Thus, if there's zx = y (fx) (gx) , it can be transformed into point free: z = y <$> f <*> g

An alternate point-free form would be to use the a -> Any monoid:

λ 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]

It's a bit longer than the Applicative solution, but I think it's a little easier to follow when you have more conditions to combine. Compare

getAny . (Any . isZero <> Any . isOne <> Any . isSquare <> Any . isPrime)

or

getAny . foldMap (Any .) [isZero, isOne, isSquare, isPrime]

and

liftA2 (||) (liftA2 (||) (liftA2 (||) isZero isOne) isSquare) isPrime

or

liftA2 (||) isZero $ liftA2 (||) isOne $ liftA2 (||) isSquare isPrime

Though to be honest, if I had lots of these to do, I'd be tempted to define <||> = liftA2 (||) and do

isZero <||> isOne <||> isSquare <||> isPrime

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM