4 ms·
I had a similar example today in the functional programming slack. > hello, quick question: i feel like there's a more elegant way to express this, but I'm str
by codygman 6y ago
I had a similar example today in the functional programming slack.
> hello, quick question:
i feel like there's a more elegant way to express this, but I'm struggling to come up with one:
foo :: (a -> Bool) -> (a -> Bool) -> a -> Bool
foo f g x = f x && g x
For those who aren't as familiar with Haskell, that is taking a function f and a function g, passing argument x to both, and then doing a binary and on their boolean result.
One answer suggested:
> You can get fancy, but the “simple” version is almost always more readable:
(not . null . f $ x) && (not . null . g $ x)
Which I find myself agreeing with in many ways, but for some reason leaves me desiring more.
The answer to that desire complements this article I think:
> if f and g returned Maybe you could have:
> f _ = Nothing
> g _ = Just ()
> x = ()
> (f x) <*> (g x)
Nothing
> import Data.Maybe
> (f x) <*> (g x) & isJust
False
> -- or to avoid <*> you can do
> liftA f g x
Nothing
If that's not clear, let me know what's confusing and I'll try to explain further
- sudomakeup 6y agoSo, I'm a bit rusty on haskell but I have some notes on a similar concept. Essentially, fmap with a twist - instead of applying the same function to a list of values, you have a list of functions that you want to evaluate on the same value. "fpam". In this case, we're dealing with a list of size 2 fpam :: [(a -> b)] -> a -> [b] fpam fns v = fns <*> pure v after that then fold the list with boolean && foo :: (a -> Bool) -> (a -> Bool) -> a -> Bool foo f g x = foldr1 (&&) ( fpam [f,g] x) or alternatively with no helper functions foo2 :: (a -> Bool) -> (a -> Bool) -> a -> Bool foo2 f g x = foldr1 (&&) ( [f,g] <*> pure x) for example Prelude> foldr1 (&&) ( [ (==3), (==4) ] <*> pure 3 ) False Alternate implementations: import Data.List import Data.Function import Control.Monad.State import Data.Foldable import Control.Arrow((>>>)) import Control.Monad.Reader import Control.Monad.List applyList :: [(a -> a)] -> a -> a applyList list = execState $ for_ list modify applyList2 :: [(a -> a)] -> a -> a applyList2 = foldr1 (>>>) fpam :: [(a -> b)] -> a -> [b] fpam fns v = fns <*> pure v fpam2 :: [(a -> b)] -> a -> [b] fpam2 fns = runReader $ forM fns reader fpam3 :: [(a -> b)] -> a -> [b] fpam3 fns v = fmap (\f -> f v) fns fpam4 :: [(a -> b)] -> a -> [b] fpam4 fns = runReaderT $ do fn <- lift fns reader fn
- codygman 6y agoAlso see this related discussion: https://www.reddit.com/r/programming/comments/ij0qzl/comment/g3dzohz https://www.reddit.com/r/programming/comments/ij0qzl/comment...
- codygman 6y agoCatching up on comments and kind of fuzzy, but this is pretty cool :) foldr1 (&&) ( [ (==3), (==4) ] <*> pure 3 )
- smegma2 6y ago`on` runs a binary function by first running a unary function on each argument. Prelude> import Data.Function (on) Prelude Data.Function> :t on on :: (b -> b -> c) -> (a -> b) -> a -> a -> c Prelude Data.Function> f = undefined :: Int -> [Int] Prelude Data.Function> g = undefined :: Int -> [Int] Prelude Data.Function> :t ((&&) `on` (not . null . ($ x))) f g ((&&) `on` (not . null . ($ x))) f g :: Bool Alternatively in Control.Arrow there's a 'fanout' operator &&&: Prelude Control.Arrow> f = undefined :: Int -> Bool Prelude Control.Arrow> g = undefined :: Int -> Bool Prelude Control.Arrow> :t uncurry (&&) . (f &&& g) uncurry (&&) . (f &&& g) :: Int -> Bool Of course it's still probably simpler to just write it out in this case.
- codygman 6y agoAlso see this related discussion: https://www.reddit.com/r/programming/comments/ij0qzl/comment/g3dzohz https://www.reddit.com/r/programming/comments/ij0qzl/comment...
- dllthomas 6y agoI'll occasionally hide the monomorphic && and define something like: class Predicate a where (&&) :: a -> a -> a not :: a -> a etc :: ... and then define the obvious instances for `Bool` and `Predicate b => a -> b` It's really nice to be able to just write `(> 6) && even` or `isAlpha || (== '_')`