-12

How to rewrite the following function in point-free style, removing the parameter x from the definition completely (the other two may stay):

between min max x = (min < x) && (x < max)

This is not an assignment, just a question. I don't know how to proceed. I can turn it into a lambda function

between min max = \x -> (min < x) && (x < max)

but this is not point-free, as x is still there. Please help.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
user2304996
  • 246
  • 2
  • 10
  • 5
    is this an assignment? what have *you* tried? BTW calling your values by names of built-in functions is confusing. Better use `minval` and `maxval`. – Will Ness Nov 20 '18 at 11:19
  • 2
    You might want to look at the documentation for `liftA2` in module `Control.Applicative` – mschmidt Nov 20 '18 at 11:56
  • 3
    There's no compelling reason to write this particular function in point-free style. This version is quite readable as-is. – chepner Nov 20 '18 at 14:05
  • You mean the reason for closure? Questions are supposed to show at least some traces of the asker's personal effort. that's what "what have you tried?" referred to. A simple "I tried this or that" with a piece of code, however nonsensical, would do to show the effort. I would normally leave it be even then but "You can rearrange parameters as you want." looked like a straight copy-paste from an assignment sheet. So I voted to close. I gather four others felt the same way. Moreover, the answers are probably not really helpful to you, and seem designed to prevent you handing them in as is. – Will Ness Nov 21 '18 at 10:32
  • But if you were to indicate where specifically were you having a difficulty, I'm sure you'd receive help here that would be much more to the point, and helpful. Otherwise, we don't know what is your difficulty here, do we. :) the specific rule is: *"questions ... must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself"*. (btw, I didn't down vote) – Will Ness Nov 21 '18 at 11:11
  • 1
    @WillNess _questions ... must include the desired behavior_ isn't it clear what I want to achieve? _and the shortest code necessary to reproduce it in the question itself_ I wrote the code of the simple function with understandable name and logic. Could you please refer to the rule, where written that I must mention what I have tried? – user2304996 Nov 21 '18 at 11:24
  • @user2304996 "isn't it clear what I want to achieve? " yes, you want a ready-made answer to your problem. "I wrote the code of the simple function" no, you haven't included *your code* aimed at solving your problem, not one bit of it. Let me give you an example: it is as if you asked: "what is the definite integral of `x^1.5` on the interval `[1..10]` (you may give me an answer as a number with two decimal places precision).". And I'm asking, "what have you tried"? Of course your question is clear, the clarity is not the problem, the lack of any trace of your effort is. – Will Ness Nov 21 '18 at 11:31
  • the rule is: *" a specific problem* [with your attempt at solving your problem] *or error and the shortest code necessary to reproduce it "*. otherwise, I'm not a lawyer. please take it to the [meta](https://meta.stackoverflow.com/). Ask there, whether this question was closed properly or not. :) – Will Ness Nov 21 '18 at 11:32
  • 4
    [This question is being discussed in Meta](https://meta.stackoverflow.com/questions/376889/was-my-question-closed-properly) – yivi Nov 21 '18 at 11:54
  • 3
    OK, so after the edit, and while the new 3rd vote for deletion is not cast, what have you tried? Where exactly is your difficulty? Have you yourself tried to find a solution? What code snippets have you written? Do you know about "operator sections"? Do you know about "combinators"? (these are googlable terms). Do you know e.g. about transforming `sin (cos x)` to `(sin . cos) x`? Do you know about "eta reduction"? – Will Ness Nov 21 '18 at 15:17
  • sorry, I don't understand. I'm trying to help here. I've cast an undelete vote. there's 2 new votes for deletion now (3rd will delete it once more). (there's also 2 votes to reopen; 3 more are needed for that to happen) ---- meantime, `\x. (minVal < x) && (x < maxVal)` is not valid Haskell, right? – Will Ness Nov 21 '18 at 15:39
  • As I said, I was trying to help. Thanks for your feedback. – Will Ness Nov 21 '18 at 16:36
  • 1
    @user2304996 "but you just can't stop" -- Will Ness didn't do anything wrong here. While we do strive to preserve as much of the meaning and intent of a question as reasonable when editing it, keep in mind that posting a question here is contributing to a community repository of questions and answers. You should think of it as something like creating a page in a public wiki. – duplode Nov 21 '18 at 19:22
  • @duplode as I see it, the question looked like a hopeless dump of an assignment, with that "you can ..." language, containing nothing except the problem statement itself. the OP didn't respond to clarification request, didn't edit. It went through some meta drama, closure, deletion and undeletion (for which I voted), so finally I had to edit to make it passable so I could vote to reopen (it was one vote away from being deleted again, and all the posters' efforts would've gone to waste). – Will Ness Nov 22 '18 at 09:14
  • @WillNess, Honestly, it is not an assignment. I just wanted to know (just curiosity) what are the options to make this function's definition in point-free style. I don't care how it look like. I wouldn't argue with you at all and wouldn't create question to meta if you would give reference to the rule I broke in the first place. Instead you gave me something not related. For me it was like you just putt it on hold, because you can (have permissions). – user2304996 Nov 22 '18 at 12:24

4 Answers4

7

It can be done using the Reader applicative:

between min max = \x. (min < x) && (x < max)
              { Convert infix comparisons to sections }
                = \x. ((min <) x) && ((< max) x)
              { Move infix (&&) to applicative style }
                = \x. (&&) ((min <) x) ((< max) x)
              { Lift to applicative style using the applicative instance of `(->) a` }
                = \x. (pure (&&) <*> (min <) <*> (< max)) x
              { Eta-reduce }
                = pure (&&) <*> (min <) <*> (< max)
              { Optionally simplify for idiomatic style }
                = (&&) <$> (min <) <*> (< max)
justkris
  • 800
  • 4
  • 15
4

Another solution (needs import of Control.Applicative):

between min max = liftA2 (&&) (min <) (max >)
mschmidt
  • 2,740
  • 4
  • 17
  • 31
  • 1
    and with `(<&&>) = liftA2 (&&)` it becomes the almost readable `(min <) <&&> (max >)`. BTW there are these definitions somewhere, `(<^) = flip fmap` and `(^>) = (<*>)`, such that it becomes the nearly identical-looking `(min <) <^(&&)^> (max >)`. – Will Ness Nov 21 '18 at 11:18
3

Using Control.Arrow we can reach this nearly obfuscated code:

(min <) &&& (< max) >>> uncurry (&&)

This relies on the predefined >>> for left-to-right composition, f &&& g = \x -> (f x, g x), and uncurrying.


pointfree.io also suggests the following unreadable code:

between = (. flip (<)) . ap . ((&&) .) . (<)
chi
  • 111,837
  • 3
  • 133
  • 218
1

By operator sections transformation,

between min max x = (min < x) && (x < max)
                  = ((&&) . (min <)) x ((< max) x)

Now this fits a pattern for S-combinator, S f g x = (f x) (g x). There are many ways of encoding it in Haskell, but the main two are via Applicative and via Arrows:

    _S f g x = (f x) (g x)
             = (f <*> g) x
             = uncurry id . (f &&& g) $ x

The second gives us

between a z = uncurry (&&) . ((a <) &&& (< z))

And the first, even more fitting

between a z = (&&) <$> (a <) <*> (< z)
            = liftA2 (&&) (a <) (< z)
            = (a <) <^(&&)^> (< z)     -- nice and visual

(<^) = flip (<$>) 
(^>) = (<*>)

But we could also fiddle with other combinators, with much less satisfactory results though,

    _S f g x = f x (g x)
             = flip f (g x) x
             = (flip f . g) x x
             = join (flip f <$> g) x
             = (flip f =<< g) x 

or

             = (f x . g) x
             = (. g) (f x) x
             = ((. g) =<< f) x

which illustrates nicely the dangers of pointlessness in the pursuit of the pointfree.

There's one more possibility that makes sense (syntactically), which is

    _S f g x = (f x) (g x)
        --   = foldr1 ($) . sequence [f,g] $ x  -- not valid Haskell

        -- sequence [f,g] x = [f x,g x]

This is not a valid Haskell in general because of the typing issues, but in our specific case it does give rise to one more valid definition, which also does seem to follow the inner logic of it nicely,

between a z = -- foldr1 ($) . sequence [(&&).(a <), (< z)] -- not OK
            = foldr1 (&&) . sequence [(a <), (< z)]        -- OK
            = and . sequence [(a <), (> z)]

because (a <) and (> z) have the same type.

Will Ness
  • 70,110
  • 9
  • 98
  • 181