motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson
When trawling the ClojureScript source, I was a little puzzled when I first 
noticed that cljs.core/apply respects the laziness of the seq its provided. 
Fogus mentioned this feature in his Clojure/west talk and it reminded me of 
my earlier puzzlement.

This choice seems to contradict Clojure's eager argument evaluation 
semantics. Is the motivation of this choice described anywhere? I'm 
guessing I am missing an obvious use case.

-- 
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

Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Alan Malloy
On Mar 27, 11:51 pm, Nathan Sorenson n...@sfu.ca wrote:
 When trawling the ClojureScript source, I was a little puzzled when I first
 noticed that cljs.core/apply respects the laziness of the seq its provided.
 Fogus mentioned this feature in his Clojure/west talk and it reminded me of
 my earlier puzzlement.

 This choice seems to contradict Clojure's eager argument evaluation
 semantics. Is the motivation of this choice described anywhere? I'm
 guessing I am missing an obvious use case.

You've misunderstood how clojure.core/apply behaves - it passes
sequences lazily as well. For example, (apply (fn f [ args] (take 3
args)) (range)) works fine, even though you pass an infinite number of
arguments to f.

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson
Your right the core apply is lazy too. The same question still remains: is 
there a use case behind apply being lazy when Clojure is otherwise a 
strictly evaluating language? Perhaps is this the intended mechanism to 
smuggle non-strictness into evaluation?

-- 
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

Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson
Actually now that I've thought about it, you couldn't mimic non-strict 
evaluation with lazy apply, so that's not a use-case. All you could provide 
is left-to-right argument non-strictness which is not sufficient. W.r.t. 
your example,  you can force evaluation the first 3 args, but you can't, 
say, force the evaluation of only the 3rd argument like in Haskell.

I use apply to leverage list processing functions to massage input to 
functions that are generally non-seq-ey. If a function is intended to 
operate on lazy sequences, it seems to me that you would pass those 
sequences in as explicit arguments, in the same manner as all the Clojure 
seq operations.

Again, we don't have the machinery to mimic non-strict evaluation so I 
don't think building functions that behave in this halfways-non-strict 
manner is obvious design. I'd like to see a function that depends on the 
left-to-right-non-strictness that lazy apply provides.

-- 
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

Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Timothy Baldridge
is there a use case behind apply being lazy when Clojure is otherwise a 
strictly evaluating language

In clojure-py we have to pass vararg arguments as tuples. So it ends
up a lot like

(to-tuple (concat args seqarg))

I always saw the seq argument in IFn as a crutch to get around the we
have too many vars to pass to this function problem. Basically, since
the JVM doesn't support lazy varargs, Clojure doesn't either.


Timothy

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Cedric Greevey
On Wed, Mar 28, 2012 at 2:13 PM, Timothy Baldridge tbaldri...@gmail.com wrote:
is there a use case behind apply being lazy when Clojure is otherwise a 
strictly evaluating language

 In clojure-py we have to pass vararg arguments as tuples. So it ends
 up a lot like

 (to-tuple (concat args seqarg))

 I always saw the seq argument in IFn as a crutch to get around the we
 have too many vars to pass to this function problem. Basically, since
 the JVM doesn't support lazy varargs, Clojure doesn't either.

Apply isn't really non-strict. If your source contains

(apply foo x y z w)

then apply, foo, x, y, z, and w will all be evaluated, in
left-to-right order, before foo is called with x, y, z, and the
contents of w as arguments. If w is a literal such as [w1 w2 w3 w4]
then w1, w2, w3, and w4 will get evaluated, after z and in
left-to-right order, before foo is called.

In other words, any side effects in a line like

(apply foo x y z [w1 w2 w3 w4])

happen in the order one would expect. And if w is really a lazy
sequence, and a lambda involved in generating it has side effects, the
fact that the side effects are in a lambda (or not even in the
(apply...) line) tells you that the side effects may be delayed.

Further, it's possible to get a kind of non-strict-analogous behavior
(side effects may be delayed, reordered, or not happen at all) in
Clojure if foo and its caller are written specially. For example,

(defn weird-and [x y]
  (if @x @y))

(weird-and (delay (long-running-test-1)) (delay (long-running-test-2)))

will only compute the second expensive test if the first didn't return
logical false. (The built-in and macro also short-circuits, but does
so by being a macro. Unlike and, weird-and could be passed to a HOF.
But the HOF would have to know to pass it delays rather than normal
booleans. You could do (reduce weird-and [(delay this) (delay that)
...]).)

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Timothy Baldridge
May be delayed

But I don't think they ever are:

user= (def oddseq (map #(do (print %) %) (range 30)))
#'user/oddseq

user= (defn foo [ args] 'd)
#'user/foo

user= (apply foo oddseq)
01234567891011121314151617181920212223242526272829d

user= (def oddseq (map #(do (print %) %) (range 1)))
#'user/oddseq

user= (apply foo oddseq)
0d

user=

I don't think it's possible to ever have apply not evaluate all its
arguments? I think this is what Nathan was saying. Given the above
example, is there anyway to not have the side effects automatically
run when invoking apply?

Timothy



-- 
“One of the main causes of the fall of the Roman Empire was
that–lacking zero–they had no way to indicate successful termination
of their C programs.”
(Robert Firth)

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson


 I don't think it's possible to ever have apply not evaluate all its
 arguments? I think this is what Nathan was saying. 


Cedric is right that apply, itself, is strict and evaluates its arguments 
as one might expect. But I'm not referring to manual thunking/delaying your 
arguments to mimic laziness in a strict language. What I'm trying to 
understand is that one is able to write a polyvariadic function that 
*doesn't* completely realize the  rest parameter (but only in the case 
that the fn is invoked via apply with a lazy seq argument). It's this 
combination of requirements that means apply should *not* construct a tuple 
(as in clojure-py) or cons arguments together into a list (as I'm currently 
doing in clojure-scheme) as these operations will cause the realization of 
the entire applied sequence.

 

 Given the above
 example, is there anyway to not have the side effects automatically
 run when invoking apply?

Yes. If you bury an effect into the lazy-seq at some position n, it won't 
be realized unless the applied function is either fixed-arity up to n, or 
chooses to force the lazy-seq up to n (modulo chunked evaluation). 

My question is what would require this sort of function? Is there a 
use-case for a function that expects to be called via 'apply' to avoid 
evaluating some of its arguments? 

(Again, this isn't a question of thunking; The user of this hypothetical 
function can apply it with a strict list or a lazy list and no code needs 
to change--I, as a caller, don't need to explicitly thunk arguments for 
laziness and the fn doesn't need to explicitly force/dereference them).

Put another way: why does apply need to promise not to realize its seq 
argument?

-- 
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

Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson
An example of lazy apply, with your foo fn:

 (apply foo (iterate inc 0))
d

-- 
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

Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Cedric Greevey
On Wed, Mar 28, 2012 at 8:32 PM, Nathan Sorenson n...@sfu.ca wrote:

 I don't think it's possible to ever have apply not evaluate all its
 arguments? I think this is what Nathan was saying.

 Cedric is right that apply, itself, is strict and evaluates its arguments as
 one might expect. But I'm not referring to manual thunking/delaying your
 arguments to mimic laziness in a strict language. What I'm trying to
 understand is that one is able to write a polyvariadic function that
 *doesn't* completely realize the  rest parameter (but only in the case
 that the fn is invoked via apply with a lazy seq argument). It's this
 combination of requirements that means apply should *not* construct a tuple
 (as in clojure-py) or cons arguments together into a list (as I'm currently
 doing in clojure-scheme) as these operations will cause the realization of
 the entire applied sequence.

I suggest that apply should, if the function has no rest argument,
realize enough of its input(s) to either be ready to call the function
or know there's at least one more argument than the function's highest
arity and throw an exception; if the function has a rest argument, it
should cons up a list of arguments until either it runs out or there's
enough for all the non-rest arguments of the highest arity overload,
then call the function (or throw) if it's exhausted its input or else
call the function with the rest argument bound to the unrealized tail
of the input.

So, if the input is (range), then:

for (defn foo [a b]) it would realize 0, 1, and 2, then throw arity.

for (defn foo [a b  r]) it would realize 0 and 1, then call foo with
(nth (range) 0) (nth (range) 1) (next (next (range))) as a, b, and r.

 My question is what would require this sort of function? Is there a use-case
 for a function that expects to be called via 'apply' to avoid evaluating
 some of its arguments?

I can't think of any offhand.

 Put another way: why does apply need to promise not to realize its seq
 argument?

(apply + some-lazy-seq-too-big-to-fit-in-main-memory)

Not avoid evaluating some of its arguments but avoid holding onto
the head in that case.

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Cedric Greevey
On Wed, Mar 28, 2012 at 7:16 PM, Timothy Baldridge tbaldri...@gmail.com wrote:
 May be delayed

 But I don't think they ever are:

 user= (def oddseq (map #(do (print %) %) (range 30)))
 #'user/oddseq

 user= (defn foo [ args] 'd)
 #'user/foo

 user= (apply foo oddseq)
 01234567891011121314151617181920212223242526272829d

 user= (def oddseq (map #(do (print %) %) (range 1)))
 #'user/oddseq

 user= (apply foo oddseq)
 0d

 user=

 I don't think it's possible to ever have apply not evaluate all its
 arguments? I think this is what Nathan was saying. Given the above
 example, is there anyway to not have the side effects automatically
 run when invoking apply?

Range is chunked, and I think the map-generated sequence inherits
that. On the other hand, I thought the chunk size was 16, and that it
didn't matter anyway if no element was ever requested. On the gripping
hand, (apply f x) calls (seq x), which realizes the first element (and
thus the first chunk, for chunked seqs) to determine if x is empty and
it should therefore return nil.

-- 
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


Re: motivation behind laziness of apply in ClojureScript

2012-03-28 Thread Nathan Sorenson


  Put another way: why does apply need to promise not to realize its seq
  argument?

 (apply + some-lazy-seq-too-big-to-fit-in-main-memory)

 Not avoid evaluating some of its arguments but avoid holding onto
 the head in that case.

I'd reach for 'reduce' in this case but that's still a valid point.
 

-- 
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