Hmm.. I think you are raising both a technical and a philosophical issue - 
what exactly should a higher-order function return when some application of 
the supplied function throws exception... The behaviors could be:
1) throw
2) return null
3) return an empty collection (do not continue after 1st failure)
4) same, but continue through all failures
5) return collection with valid applications only
What is good behavior? We should have as little uncertainly as possible... 
I have to admit my first thought is 'just don't allow it to throw, return 
null instead'. This would allow to still collect results (where it does not 
throw), and they will be at the matching index locations...

Now the technicality. The map code I see for 1.4 is trying to append result 
of all invocations to a buffer upfront. And each time it will fail, and the 
buffer will be empty, hence empty result Source is a chunked collection and 
there is only one chunk, it will be done immediately, and not on subsequent 
calls - laziness has not started yet  

Please contrast it with mapv, which results in a vector, not sequence
user=> (def mapped (mapv (fn [_] (throw (Exception.))) [1 2 3]))
Exception   user/fn--1 (NO_SOURCE_FILE:1)
user=> mapped
#<Unbound Unbound: #'user/mapped>

Now the lazy-seq example - maybe the difference in behavior can be 
explained by the fact that lazy-seq caches the result of body evaluation 
and will keep returning it. Since there is an exception during each 
evaluation, the caching does not quite happen, and it is re-evaluated?

Should the behavior be the same in all 3 cases? I think so, at least for 
consistency (unsure it can be achieved, though). But throwing in function 
passed to map  really is something you should not do, and I would recommend 
to change map function to return a null or throw -- to let you know that 
your code can cause odd behavior. BTW I am not aware of any clojure book 
that alerts you to that. 

Also, maybe the implementation of map function should catch your exception 
internally and produce a null, and return a sequence of nulls?

-Julian

On Sunday, December 2, 2012 5:58:08 AM UTC-8, Hank wrote:
>
> I'm mapping a function that throws an exception over a collection:
>
> => (def mapped (map (fn [_] (throw (Exception.))) [1 2 3]))
> #'user/mapped
>
> 'map' is lazy so we're not expecting to see the exception until we're 
> trying to access the result:
> => mapped
> Exception   user/fn--709 (NO_SOURCE_FILE:1)
>
> All good, let's do that again:
> => mapped
> ()
>
> Whoops! Is this by design? Why? Where does that empty sequence come from?
>
> 'map' is really just calling the lazy-seq macro but doing this with lazy 
> seq works just fine:
> => (def lazy (lazy-seq (throw (Exception.))))
> #'user/lazy
> => lazy
> Exception   user/fn--733 (NO_SOURCE_FILE:1)
> => lazy
> Exception   user/fn--733 (NO_SOURCE_FILE:1)
>
> Same exception over and over again as it should be. I stared at the 
> implementations of 'map' and lazy-seq/LazySeq for some hours but I really 
> can't see it.
>
> Cheers
> -- hank
>
>

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

Reply via email to