why (> 2) returns true

326 views
Skip to first unread message

Jeremy Vuillermet

unread,
Sep 15, 2014, 3:46:53 AM9/15/14
to clo...@googlegroups.com
Could it return a (partial > 2) ?

Alan Forrester

unread,
Sep 15, 2014, 4:29:42 AM9/15/14
to clo...@googlegroups.com
On 15 September 2014 08:46, Jeremy Vuillermet
<jeremy.v...@gmail.com> wrote:
> Could it return a (partial > 2) ?

http://clojuredocs.org/clojure_core/clojure.core/%3E

If you look at the source code near the bottom of the page, you will
find that it specifies that when you give > a single argument it
always returns true.

Also, partial returns a function not a Boolean.

Alan

Phillip Lord

unread,
Sep 15, 2014, 8:34:56 AM9/15/14
to clo...@googlegroups.com


Jeremy Vuillermet <jeremy.v...@gmail.com> writes:

> Could it return a (partial > 2) ?


Because > works with n args and not just two.

(> 2) => (partial > 2)

then why not

(> 2 3) =? (partial > 2 3)

when is the sensible place to stop?

Now, if > took at most two args, this would be a sensible thing.

As far as I can see, with > defined as it is you get the win that

(> 10 9 8 7 6 5)

works sensible but get a counter-intuitive behaviour for

(> 1)

Gains and losses.

Phil

Alan Forrester

unread,
Sep 15, 2014, 9:45:05 AM9/15/14
to clo...@googlegroups.com
On 15 September 2014 13:44, Kalina Todorova <kalinaly...@gmail.com> wrote:
> I didn't actually think that they have actually hard-coded it to true.
>
> It makes sense from logical stand point to return true but hard-coding it I am not sure that is the best approach here.

In the source code, which is available here

http://clojuredocs.org/clojure_core/clojure.core/%3E

the value for one argument is set to true:

(defn >
"Returns non-nil if nums are in monotonically decreasing order,
otherwise false."
{:inline (fn [x y] `(. clojure.lang.Numbers (gt ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (gt x y)))
([x y & more]
(if (> x y)
(if (next more)
(recur y (first more) (next more))
(> y (first more)))
false)))

Alan

Jeremy Vuillermet

unread,
Sep 15, 2014, 10:42:24 AM9/15/14
to clo...@googlegroups.com
Thanks, that' clearer.
Also I didn't take time to read the docstring
  "Returns non-nil if nums are in monotonically decreasing order, 
  otherwise false." 

so I guess [2] is monotonically decreasing and increasing at the same time.
Maybe I just read too much about transducers and now I try -1 arity everywhere

Phillip Lord

unread,
Sep 15, 2014, 12:04:48 PM9/15/14
to clo...@googlegroups.com
Jeremy Vuillermet <jeremy.v...@gmail.com> writes:

> Thanks, that' clearer.
> Also I didn't take time to read the docstring
> "Returns non-nil if nums are in monotonically decreasing order,
> otherwise false."
>
> so I guess [2] is monotonically decreasing and increasing at the same
> time.


Yeah, so the same list can be both decreasing and increasing at the same time.

user> (def l '(1))
#'user/l
user> (apply < l)
true
user> (apply > l)
true

So, < and > are not mutually exclusive. Numerically counter-intuitive, I
think, although logically expected.


> Maybe I just read too much about transducers and now I try -1 arity
> everywhere


Basically, you were expecting a form of curry. I think that would make
sense, except that > is variadic. Got to know where to stop with
curries in functional programming as in life.

Phil

Paweł Sabat

unread,
Sep 15, 2014, 3:33:15 PM9/15/14
to Clojure
No marco is returned.

=> (type (> 2))
java.lang.Boolean

And from here
https://github.com/clojure/clojure/blob/028af0e0b271aa558ea44780e5d951f4932c7842/src/clj/clojure/core.clj#L1029
you can see, that with one parameter, there is always returned true.


noniwoo

2014-09-15 9:46 GMT+02:00 Jeremy Vuillermet <jeremy.v...@gmail.com>:
> Could it return a (partial > 2) ?
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Robert Tweed

unread,
Sep 16, 2014, 2:13:59 PM9/16/14
to clo...@googlegroups.com
On 15/09/2014 13:34, Phillip Lord wrote:
Jeremy Vuillermet <jeremy.v...@gmail.com> writes:
Could it return a (partial > 2) ?

Because > works with n args and not just two. 

The question was why and yours is the best attempt to answer that, but I think, slightly off the mark.

Firstly, the fact that > is variadic doesn't negate the fact that a single argument is a special case. It could quite easily return a partial in that case and that case only. It doesn't buy you much compared to doing it explicitly, but it could.

If you don't believe it's a special case, consider what (> 1 2 3) or (> 3 2 1) are equivalent to. The first is always false, no matter now many arguments you add, so it could correctly evalutate to either false, or (fn ([] false) ([& args] false)) - putting the ambiguity of that to one side for a moment. OTOH, the second form evaluates to either true, or (fn ([] true) ([& args] (apply > 1 args))). Again there's a problem of ambiguity about which of those you might mean when there are two or more arguments, but we can assume those would never return a partial by default.

What this is intended to illustrate is that any list of multiple arguments can either be collapsed to a single argument (the rightmost one), or else the expression must simply be false. That makes the one-argument form a special case because any multi-argument form that you might want as a partial can be expressed as a single-argument that resolves to the same thing.

However...

There is a much more serious downside to returning partials implicitly. When considering < a and > as variadic functions rather than binary operators, I prefer to think of them as sorted-ascending? and sorted-descending? functions instead of less-than and greater-than. This is both more technically correct and makes reading code more intuitive than mentally expanding (> a b c ...) to (a > b) && (b > c) && ....

If you think of them this way (and in fact, the docstring defines the functions this way, so you should), you ought to be able to pass any list of arguments and get a sensible result that tells you whether or not that list is in the desired order. By definition, a list containing exactly one element must be in order, therefore the operators must return true, as they do now.

So clearly, returning a partial instead of a result would result in the loss of useful functionality. The only gain is not having to type the word "partial" so it's not a great trade-off.

In writing this, I thought I'd better also test what (>) and (<) evaluate to, because by the above definition, those should also evaluate to true. Unfortunately, at least in v1.6, they throw an arity error. IMO, by the same logic that says a single argument is valid, no arguments should be valid too. Consider the following perfectly valid use-case:

(def in-order? #(apply > %)) ; Seems obviously correct, no?
(def items [])               ; Maybe get this from a database; might be empty
(in-order? items)            ; Oops!
; ArityException Wrong number of args (0)...

This should perhaps be considered a bug. I suppose it depends on your definition of "in order" for an empty set, but if we say that the most sensible definition is based on whether the invariant "sort(x)=x" holds true, then true is the correct result for the empty set.

One final observations is that you could actually implement something like this, which does allow partial application and yet also "just works" (mostly; caveat to follow):

(defn >>
    ([] (fn ([] true) ([& args] (apply > args))))
    ([x] (fn ([] true) ([& a] (apply > x a))))
    ([x & args] (if (apply > x args) (fn ([] true) ([& a] (apply > (last args) a))) false))
    )

This returns either a partial, or false. This almost works perfectly. Because a function is not a falsey value in Clojure, you can use the result of this as a predicate, or you can use it as a partial application. However, it doesn't work when the return value is false, because Clojure will not evaluate (false); it just throws an exception. If (false) simply ignored its arguments and evaluated to false, this code would work 100%. I'm sure there are Lisps out there that would do that.

See how it works perfectly ...<brushes carpet> apart from that troublemaking (false) case...
(if (>>) true false)     ; true
(if (>> 1) true false)   ; true
(if (>> 2 1) true false) ; true
(if (>> 1 2) true false) ; false
((>>) 2 1)               ; true
((>>) 1 2)               ; false
((>> 1) 0)               ; true
((>> 1) 2)               ; false
((>> 2 1) 0)             ; true
((>> 2 1) 3)             ; false
((>> 1 2) 3)             ; BOOM!
((>> 1 2) 0)             ; BOOM!
I'm not suggesting that anyone actually try to use something like this in a real project. It's a pretty dumb idea, but I thought it was also mildly interesting. It might look cute but it's most likely horribly inefficient and, let's be honest, probably a lot more confusing than just typing the word "partial" when you want a partial.

I'm also inclined to think the non-evaluation of (false) is probably correct because the likelihood that it indicates a bug is probably a lot higher than the number of cases where it might be convenient, like this one.

- Robert

Phillip Lord

unread,
Sep 17, 2014, 5:52:25 AM9/17/14
to clo...@googlegroups.com
Robert Tweed <fistful.o...@gmail.com> writes:


> In writing this, I thought I'd better also test what (>) and (<) evaluate to,
> because by the above definition, those should also evaluate to true.
> Unfortunately, at least in v1.6, they throw an arity error. IMO, by the same
> logic that says a single argument is valid, no arguments should be valid too.
> Consider the following perfectly valid use-case:
>
> (def in-order? #(apply > %)) ; Seems obviously correct, no?
>
> (def items []) ; Maybe get this from a database; might be empty
>
> (in-order? items) ; Oops!
>
> ; ArityException Wrong number of args (0)...
>
>
> This should perhaps be considered a bug. I suppose it depends on your
> definition of "in order" for an empty set, but if we say that the most
> sensible definition is based on whether the invariant "sort(x)=x" holds true,
> then true is the correct result for the empty set.


I agree that according to the docstring,

"Returns non-nil if nums are in monotonically decreasing order,
otherwise false."

then both:

(>)
(<)

should return non-nil since the numbers are in monotonicaly decreasing
order. This is what is known as a correct, but uncooperative answer.
So wrt the definition, the zero-arg call is special cased (i.e. it
throws an arity exception).

So, why not special case 1 arg as well, and have that except? It's a
reasonable question. I would submit a bug report and see if anyone else
agrees. Something is wrong for sure. Either (> x) should throw arity, or
(>) should return true, or the docstring should be changed to note the
special case for (>).


Phil

Herwig Hochleitner

unread,
Sep 17, 2014, 7:32:52 AM9/17/14
to clo...@googlegroups.com
2014-09-17 11:51 GMT+02:00 Phillip Lord <philli...@newcastle.ac.uk>:

So, why not special case 1 arg as well, and have that except? It's a
reasonable question. I would submit a bug report and see if anyone else
agrees. Something is wrong for sure. Either (> x) should throw arity, or
(>) should return true, or the docstring should be changed to note the
special case for (>).


Totally agree, please post ticket number, so that we can favorite!

Phillip Lord

unread,
Sep 17, 2014, 9:40:17 AM9/17/14
to clo...@googlegroups.com

Ashton Kemerling

unread,
Sep 17, 2014, 10:28:34 AM9/17/14
to clo...@googlegroups.com, clo...@googlegroups.com
I wouldn't be surprised if the 1 arg form is to help people who use > along with apply, just in case the list is only 1 element long. 

Robert Tweed

unread,
Sep 17, 2014, 11:00:14 AM9/17/14
to clo...@googlegroups.com
On 17/09/2014 15:28, Ashton Kemerling wrote:
> I wouldn't be surprised if the 1 arg form is to help people who use >
> along with apply, just in case the list is only 1 element long.
That is precisely why it should do the same thing with zero arguments,
which is what happens when you use apply with an empty list.

I just added a comment to that effect to the bug report. IMO it should
be documentation change in 1.6.x and a fix in 1.7+ - just on the off
chance that some crazy person has written code like this:

(def lst [])
(try (apply > lst) (catch Exception _ :empty))

This code is crazy because it can return true, false or :empty. If you
do that, you're doing it wrong. Still, it's not nice to break things
like that in a sub-minor revision unless there's a serious security issue.

- Robert

Phillip Lord

unread,
Sep 17, 2014, 11:12:11 AM9/17/14
to clo...@googlegroups.com

Same argument applies (er...) to the zero element case.

Phil
--
Phillip Lord, Phone: +44 (0) 191 222 7827
Lecturer in Bioinformatics, Email: philli...@newcastle.ac.uk
School of Computing Science, http://homepages.cs.ncl.ac.uk/phillip.lord
Room 914 Claremont Tower, skype: russet_apples
Newcastle University, twitter: phillord
NE1 7RU
Reply all
Reply to author
Forward
Message has been deleted
0 new messages