Haskell Function Composition - (a & # 8594; b) & # 8594; (a & # 8594; c) & # 8594; (b & # 8594; c & # 8594; d) & # 8594; (a & # 8594; d)

I would like to learn how to do the following in point-free:

withinBounds :: [Int] -> Bool withinBounds xs = (all (>= 0) xs) && (all (<= 8) xs) 

I understand that it is better to write it like this for readability / common sense, but I would like to know more about how I can create functions. I scratch my head how I can do this. Full (extended?) Type signature

 [Int] -> ([Int] -> Bool) -> ([Int] -> Bool) -> (Bool -> Bool -> Bool) -> Bool 

The typical song signature I'm trying to jump to is

 (a -> b) -> (a -> c) -> (b -> c -> d) -> (a -> d) 

I wrote the following notes in the form of a bastard lambda. If there is a way to simplify the problem with lambda calculus a little, it would be great if this could also be explained:

 \ L@ [] -> \ f1@ ([] -> Bool) -> \ f2@ ([] -> Bool) -> \ f3@ (Bool -> Bool -> Bool) -> f3.(f1.L).(f2.L) 

In the above example . - application, @ capture is performed (so f3 is another name for (Bool β†’ Bool β†’ Bool)). Many thanks.

Edit: I know that this is not the most optimal or reusable code, and I know that turning this into contactless makes it worse in terms of readability, etc. To clarify, I ask how I can turn it into a pointless , because I want to know more about haskell and composition .

Edit2: Really good SO contactless contact

+7
lambda haskell function-composition pointfree
source share
4 answers

Fulfilling the end result around the whole question, I think I will probably write it like this:

 import Data.Ix withinBounds = all (inRange (0, 8)) 

Of course, this is a bit of a hiccup, since then one could naturally ask how to implement inRange problem-free way. If you cannot use inRange , I would execute it like this:

 withinBounds = all (liftA2 (&&) (>=0) (<=8)) 

It uses a reader application to provide one argument to two functions. liftA2 is your requested combining function, although the arguments are inverted:

 requested :: (a -> b) -> (a -> c) -> (b -> c -> d) -> (a -> d) liftA2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d) 
+2
source share

You can use the fact that the function is applicative. and write withinBounds as follows:

 withinBounds = pure (&&) <*> all (>= 0) <*> all (<= 8) 

Or so:

 withinBounds = (&&) <$> all (>= 0) <*> all (<= 8) 

Here you can read about Applicatives here and here.

+9
source share

There is a class that is mainly intended for meaningless compositions † with several β€œchannels”: Arrow . If you are determined to do everything pointwise, then this is IMO - the way. The ugly bit about this is that you constantly need unmanaged functions:

 import Control.Arrow withinBounds = all (>=0) &&& all (<=8) >>> uncurry (&&) 

How best to understand this with a chart:

  all (>=0) ──── β•± β•² ──── &&& >>> uncurry (&&) ─── β•² β•± all (<=8) ──── 

† Arrow works in a generic setting; not just for Hask functions, but for any suitable category. But it is useful enough to apply it only to functions.

+9
source share

Note that x <= 8 if and only if 8-x> = 0, therefore, using only the prelude, we can write

 withinBounds :: [Int] -> Bool withinBounds = all $ (all (>=0)) . (zipWith (+) [0,8]) . (zipWith (*) [1,-1]) . (replicate 2) 

Basically, I just map x to [x,x] , then to [x,8-x] , and then I require the two to be> = 0 at the same time.

Of course, as pointed out in the comments, you can also make parameters a,b to reuse them later.

Hope this helps.

-2
source share

All Articles