On 15/09/2014 13:34, Phillip Lord wrote:
Jeremy Vuillermet <jeremy.vuiller...@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
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.