Re: Consistency of the API

2009-11-11 Thread Kresten Krab Thorup


On Nov 10, 2:28 am, John Harrop  wrote:
> (I suppose T and F are static fields holding java.lang.Booleans, and this
> code was written pre-autoboxing?)

It's still much faster to use a "pre-boxed Boolean" than to create a
new boxed value every time around.

Kresten

-- 
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: Consistency of the API

2009-11-10 Thread Rich Hickey
On Mon, Nov 9, 2009 at 3:31 PM, Mark Engelberg  wrote:
>
> 2009/11/9 Tiago Antão :
>> What is the rationale for even? and contains? having different
>> behaviors for the exact same error (ie, one throws the other works
>> fine and just returns false on a type error)? From a design
>> perspective this seems to increase the cognitive load to programmers
>> without any (apparent) reason.
>>
>
> I imagine the rationale is efficiency.  Every core function could
> conceivably do a number of runtime checks to make sure that each input
> is the right kind of type, and then Clojure might feel more sluggish.
> So instead, the core functions just worry about what to do for the
> appropriate inputs.  If you pass a bogus input, the consequence
> depends entirely on how that particular function was coded.  It might
> return a spurious result, or it might error.  There are numerous
> examples of this in the Clojure API.
>

Efficiency is certainly part of it. However, it is not spurious.
Errors will become exceptions, not random nonsense. Some things one
might consider errors are not considered as such, and that is quite a
different thing.

One presumption here is that this is a simple matter of types. It is
not (simple). There are some functions whose domain is constrained by
a known/fixed (family of) type(s), but many that are not. even? is
(Numbers), contains? is not (open set of associative-like things,
false otherwise). This type-openness of many Clojure core functions is
one key to its power. I'm sure making contains?/get tolerant of
non-associative things gave us benefits elsewhere (i.e. made
associative destructuring tolerant of, and thus useful upon,
heterogeneous sequences, only some of whose elements were
associative). If that were not the case, then any code needed to
handle that would need a predicate for an open set in order to filter
first. Such predicates are extremely icky to construct and maintain,
and, brittle to rely upon.

The work I am doing on protocols may help out quite a bit here, as
they will provide a unifying point for open abstractions, encompassing
explicit type relationships (inheritance form interfaces),
superimposed capabilities (e.g. the way Strings become seq-able), and
open functional extension (a la multimethods), in a way such that
asking (implements? protocol x) will become an open type predicate.

> It wouldn't surprise me if there are a number of programmers who would
> be turned off by the ease with which one can shoot yourself in the
> foot without getting any kind of error message, but in practice I
> haven't gotten bit by this yet.
>

Everyone would like more help from a system when they have made a
mistake. But everyone needs to have a realistic view about what can be
detected, and at what cost, (especially to produce a revelatory
message), as well as a general understanding of open functions. I
imagine at some point we may have a diagnostic mode, with many
(expensive) runtime checks, and correspondingly more detailed
error/mistake reports. But you are right, the emphasis right now is on
making correct programs work well.

Rich

-- 
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

> How does it efficiently deal with when the list-part has been completely
> consumed?

Well, the latest code is here:
http://github.com/richhickey/clojure/blob/master/src/jvm/clojure/lang/PersistentQueue.java
I don't know whether it has changed since 1.0.
Just look in the src/jvm/clojure/lang directory of your clojure distribution.

But basically, when the front list is exhausted, the front part
becomes the seq of the vector, and the rear part becomes an empty
vector ready to accept more elements.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread John Harrop
On Mon, Nov 9, 2009 at 10:37 PM, Mark Engelberg wrote:

> Yes, it's in Clojure 1.0, it just doesn't have a convenient name.
>
> So give it a convenient name like this:
> (def empty-queue clojure.lang.PersistentQueue/EMPTY)
>
> and then you're ready to go.
>
> conj, peek, pop, into and all the other sequence-based functions work
> the way you'd expect.
>
> The implementation cleverly uses a list for the first part of the
> queue, and a vector for the second part of the queue, and is efficient
> and persistent.


How does it efficiently deal with when the list-part has been completely
consumed?

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

Yes, it's in Clojure 1.0, it just doesn't have a convenient name.

So give it a convenient name like this:
(def empty-queue clojure.lang.PersistentQueue/EMPTY)

and then you're ready to go.

conj, peek, pop, into and all the other sequence-based functions work
the way you'd expect.

The implementation cleverly uses a list for the first part of the
queue, and a vector for the second part of the queue, and is efficient
and persistent.

On Mon, Nov 9, 2009 at 7:22 PM, John Harrop  wrote:
> There is one? There's no corresponding API in clojure.core, at least not in
> Clojure 1.0.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread John Harrop
On Mon, Nov 9, 2009 at 8:53 PM, Mark Engelberg wrote:

> On Mon, Nov 9, 2009 at 5:41 PM, John Harrop  wrote:
> > In the meantime, the main thing still missing from Clojure is a
> convenient
> > queue.
>
> What's wrong with clojure.lang.PersistentQueue?


There is one? There's no corresponding API in clojure.core, at least not in
Clojure 1.0.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread John Harrop
On Mon, Nov 9, 2009 at 8:41 PM, John Harrop  wrote:

> In the meantime, the main thing still missing from Clojure is a convenient
> queue. Lists and vectors both add and remove efficiently only at one end,
> and at the same end for add and remove in both cases. Doubly-linked lists
> can't be made persistent without massive problems, but laziness has its own
> issues:
>
> (defn queue-peek [q] (first q))
> (defn queue-pop [q] (rest q))
> (defn queue-push [q obj] (concat q [obj]))
>
> (let [q (reduce queue-push nil (range 100))]
>   (reduce (fn [_ q] (queue-pop q)) nil q))
> #
>

Solved.

(defn queue-peek [[v i]] (nth v i))
(defn queue-pop [[v i]]
  (if (> (* 2 i) (count v))
[(vec (drop (inc i) v)) 0]
[(assoc v i nil) (inc i)]))
 (defn queue-push [[v i] obj] [(conj v obj) i])

user=> (reduce queue-push [[] 0] (range 20))
[[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 0]
user=> (queue-peek (nth (iterate queue-pop (reduce queue-push [[] 0] (range
20))) 14))
14
user=> (take 20 (iterate queue-pop (reduce queue-push [[] 0] (range 20
([[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 0]
 [[nil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 1]
 [[nil nil 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 2]
 [[nil nil nil 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 3]
 [[nil nil nil nil 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 4]
 [[nil nil nil nil nil 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 5]
 [[nil nil nil nil nil nil 6 7 8 9 10 11 12 13 14 15 16 17 18 19] 6]
 [[nil nil nil nil nil nil nil 7 8 9 10 11 12 13 14 15 16 17 18 19] 7]
 [[nil nil nil nil nil nil nil nil 8 9 10 11 12 13 14 15 16 17 18 19] 8]
 [[nil nil nil nil nil nil nil nil nil 9 10 11 12 13 14 15 16 17 18 19] 9]
 [[nil nil nil nil nil nil nil nil nil nil 10 11 12 13 14 15 16 17 18 19]
10]
 [[nil nil nil nil nil nil nil nil nil nil nil 11 12 13 14 15 16 17 18 19]
  11]
 [[12 13 14 15 16 17 18 19] 0]
 [[nil 13 14 15 16 17 18 19] 1]
 [[nil nil 14 15 16 17 18 19] 2]
 [[nil nil nil 15 16 17 18 19] 3]
 [[nil nil nil nil 16 17 18 19] 4]
 [[nil nil nil nil nil 17 18 19] 5]
 [[18 19] 0]
 [[nil 19] 1])

As you can see, it uses a vector internally, plus an internal pointer to
where the current "first" element is. Consumed elements are nulled out so if
the queue is used to hold very large objects, stale references to these are
not retained. If more than half the present length of the vector is consumed
entries, the vector is copied to a smaller vector with no consumed entries,
mirroring how things like ArrayList grow by doublings and ensuring the
copying cost is amortized O(1) if there's a long run of queue-consumption.

The queue is persistent and immutable though, much as the underlying vector
is. (Question: can Clojure's vector hold onto stale references in parts that
are unused? Or does it null out array cells that are not part of any
currently existing vector? If the latter, how does it know -- finalizers?
ReferenceQueues?)

As for why I didn't use

(defn queue-peek2 [q] (first q))
(defn queue-pop2 [q] (subvec q 1))
(defn queue-push2 [q obj] (conj q obj))

instead:

user=> (nth (iterate #(subvec (into % [1 2 3]) 3) [0 0 0]) 10)
#<10 minutes of nothing happening>
#

Subvec appears to blow up the heap if you keep growing at one end and
chopping at the other for long enough, unlike copying the vector entirely.
Meanwhile:

user=> (nth (iterate #(queue-pop (queue-push % 'foo)) [['foo 'foo] 0])
10)
#<30 minutes of nothing happening>
[[foo foo] 0]

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

On Mon, Nov 9, 2009 at 5:54 PM, David Brown  wrote:
> Perhaps the GHC Data.Sequence library could be ported.  It's based on
> 2-3 finger trees, and allows efficient adding and removal from either
> end of the sequence.

I've tried porting finger trees to Scheme before, and although it is
efficient in an algorithmic sense, I found the constants involved to
be so high, performance was very poor.  Perhaps it was a fault with my
port, but I've become quite skeptical of the value of finger trees.

Anyway, I've been satisfied with clojure.lang.PersistentQueue for queues.

I often want a priority queue, but John Harrop addressed that need not
long ago.  Before that, I would sometimes just use Clojure's sorted
set.

Sometimes I still want a persistent deque, but I haven't needed it
quite badly enough to warrant coding one myself.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread David Brown

On Mon, Nov 09, 2009 at 05:54:36PM -0800, David Brown wrote:

>Depending on use behavior, you can also make a decent lazy queue just
>out a two lists, where you reverse and append whenever the source side
>fills up.

Ok, this is what PersistentQueue is, except without the reverse and
append, so it actually performs well.

David

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread David Brown

On Mon, Nov 09, 2009 at 05:53:28PM -0800, Mark Engelberg wrote:
>
>On Mon, Nov 9, 2009 at 5:41 PM, John Harrop  wrote:
>> In the meantime, the main thing still missing from Clojure is a convenient
>> queue.
>
>What's wrong with clojure.lang.PersistentQueue?

The only clojure constructor I could find for this is in
clojure.contrib.accumulators.  But, once you have the empty one
'clojure.lang.PersistentQueue/EMPTY' you can use it pretty well with
the rest of the language.

Perhaps 'empty-queue' should be moved into core?

David

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread David Brown

On Mon, Nov 09, 2009 at 08:41:25PM -0500, John Harrop wrote:

>In the meantime, the main thing still missing from Clojure is a convenient
>queue. Lists and vectors both add and remove efficiently only at one end,
>and at the same end for add and remove in both cases. Doubly-linked lists
>can't be made persistent without massive problems, but laziness has its own
>issues:

Perhaps the GHC Data.Sequence library could be ported.  It's based on
2-3 finger trees, and allows efficient adding and removal from either
end of the sequence.

Depending on use behavior, you can also make a decent lazy queue just
out a two lists, where you reverse and append whenever the source side
fills up.

David

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

On Mon, Nov 9, 2009 at 5:41 PM, John Harrop  wrote:
> In the meantime, the main thing still missing from Clojure is a convenient
> queue.

What's wrong with clojure.lang.PersistentQueue?

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread John Harrop
On Mon, Nov 9, 2009 at 8:28 PM, John Harrop  wrote:

> Why not:
>
> static public Object contains(Object coll, Object key){
> if(coll == null)
> return F;
> else if(coll instanceof Map)
> return ((Map) coll).containsKey(key) ? T : F;
> else if(coll instanceof IPersistentSet)
> return ((IPersistentSet) coll).contains(key) ? T : F;
> else if(key instanceof Number && (coll instanceof String ||
> coll.getClass().isArray())) {
> int n = ((Number) key).intValue();
> return n >= 0 && n < count(coll);
> }
> return F;
> }
>
> instead?
>

Sort of answered my own question:

user=> (ancestors (class [1 2 3]))
#{clojure.lang.Associative java.lang.Comparable java.lang.Iterable
  clojure.lang.IPersistentVector java.util.RandomAccess clojure.lang.IMeta
  clojure.lang.IObj clojure.lang.IPersistentCollection
  clojure.lang.Sequential clojure.lang.IFn clojure.lang.Seqable
  java.util.concurrent.Callable java.io.Serializable clojure.lang.Streamable
  clojure.lang.AFn clojure.lang.Obj clojure.lang.IPersistentStack
  clojure.lang.Counted java.util.Collection java.lang.Object java.util.List
  clojure.lang.Reversible clojure.lang.APersistentVector java.lang.Runnable}

Looks like Associative doesn't inherit Map for some reason. I wonder why
not? There's no reason for it not to, with IPersistentVector for example
functioning as a Map. Since none of the Map modifying
operations would be supported it's not like someone could add a new mapping
to [1 2 3] like {500, "foo"} or {-12, 'bar} or something like that that
would screw things up.

On the other hand,

static public Object contains(Object coll, Object key){
if(coll == null)
return F;
else if(coll instanceof Map)
return ((Map) coll).containsKey(key) ? T : F;
else if(coll instanceof IPersistentSet)
return ((IPersistentSet) coll).contains(key) ? T : F;
else if(key instanceof Number && (coll instanceof String ||
coll.getClass().isArray() ||
coll instanceof Collection)) {
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll);
}
return F;
}

catches IPersistentVector and friends with strings and arrays in clause 3,
and neatly handles maps, sets, and list-like collections in three
cleanly-separated clauses, instead of mixing maps and some list-like
collections.

In the meantime, the main thing still missing from Clojure is a convenient
queue. Lists and vectors both add and remove efficiently only at one end,
and at the same end for add and remove in both cases. Doubly-linked lists
can't be made persistent without massive problems, but laziness has its own
issues:

(defn queue-peek [q] (first q))
(defn queue-pop [q] (rest q))
(defn queue-push [q obj] (concat q [obj]))

(let [q (reduce queue-push nil (range 100))]
  (reduce (fn [_ q] (queue-pop q)) nil q))
#

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread John Harrop
On Mon, Nov 9, 2009 at 5:50 PM, Alex Osborne  wrote:

> Mark Engelberg wrote:
> > 2009/11/9 Tiago Antão :
> >> What is the rationale for even? and contains? having different
> >> behaviors for the exact same error (ie, one throws the other works
> >> fine and just returns false on a type error)?
>
> > I imagine the rationale is efficiency.
>
> Here's the function from clojure/lang/RT.java:
>
> static public Object contains(Object coll, Object key){
> if(coll == null)
> return F;
> else if(coll instanceof Associative)
> return ((Associative) coll).containsKey(key) ? T : F;
> else if(coll instanceof IPersistentSet)
> return ((IPersistentSet) coll).contains(key) ? T : F;
> else if(coll instanceof Map) {
> Map m = (Map) coll;
> return m.containsKey(key) ? T : F;
> }
> else if(key instanceof Number && (coll instanceof String ||
> coll.getClass().isArray())) {
> int n = ((Number) key).intValue();
> return n >= 0 && n < count(coll);
> }
> return F;
> }
>

Why not:

static public Object contains(Object coll, Object key){
if(coll == null)
return F;
else if(coll instanceof Map)
return ((Map) coll).containsKey(key) ? T : F;
else if(coll instanceof IPersistentSet)
return ((IPersistentSet) coll).contains(key) ? T : F;
else if(key instanceof Number && (coll instanceof String ||
coll.getClass().isArray())) {
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll);
}
return F;
}

instead?

Oh, and who else finds it odd that the above implies that

user=> (contains? [1 2 3] 0.71)
true

when in actuality

user=> (contains? [1 2 3] 0.71)
false

despite

user=> (.intValue 0.71)
0
user=> (contains? [1 2 3] 0)
true

?

(I suppose T and F are static fields holding java.lang.Booleans, and this
code was written pre-autoboxing?)

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Richard Newman

> Right, strings and vectors can be thought of as either collections, or
> as associative mappings from integers to characters/objects.
> contains? treats them as associative mappings.  Yes, it's unintuitive,
> but it has a certain degree of internal consistency.

This certainly encourages you to use sets whenever you want, um, set- 
like behavior...

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

2009/11/9 Tiago Antão :
> But the end result with strings and vectors is a tad unintuitive...

Right, strings and vectors can be thought of as either collections, or
as associative mappings from integers to characters/objects.
contains? treats them as associative mappings.  Yes, it's unintuitive,
but it has a certain degree of internal consistency.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Tiago Antão

On Mon, Nov 9, 2009 at 10:20 PM, John Harrop  wrote:
> It seems to treat strings as it does vectors, seeing if an index is in
> bounds or not. It doesn't treat symbols as anything though.
> The clojure.contrib.seq-utils/includes? function gives true for "foo" and


I did not want to make this a discussion about contains? per se, but
about the general design philosophy about dealing with type erros
(which I think is a bigger issue, IMHO).

But it is a bit difficult to avoid noticing that contains? is a bit
unintuitive, so to say.

Even with vectors
(contains? [1 5] 5) being false sounds somewhat strange.

I understand the initial intent, like this:
(contains? {'a 5} 5) being false

But the end result with strings and vectors is a tad unintuitive...

Tiago

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Alex Osborne

Mark Engelberg wrote:
> 2009/11/9 Tiago Antão :
>> What is the rationale for even? and contains? having different
>> behaviors for the exact same error (ie, one throws the other works
>> fine and just returns false on a type error)?

> I imagine the rationale is efficiency.  

Here's the function from clojure/lang/RT.java:

static public Object contains(Object coll, Object key){
 if(coll == null)
 return F;
 else if(coll instanceof Associative)
 return ((Associative) coll).containsKey(key) ? T : F;
 else if(coll instanceof IPersistentSet)
 return ((IPersistentSet) coll).contains(key) ? T : F;
 else if(coll instanceof Map) {
 Map m = (Map) coll;
 return m.containsKey(key) ? T : F;
 }
 else if(key instanceof Number && (coll instanceof String ||
 coll.getClass().isArray())) {
 int n = ((Number) key).intValue();
 return n >= 0 && n < count(coll);
 }
 return F;
}


That last return could be changed to a throw and it wouldn't make things 
any slower (in the non-error case).  Note that 'get' always behaves the 
same way, so I guess it's probably intentional for associative lookups, 
I can't see why though.

   (get 3 3)
   => 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: Consistency of the API

2009-11-09 Thread John Harrop
Even more interesting is the behavior of contains? when passed strings:

user=> (contains? "foo" \o)
false
user=> (contains? "foo" 2)
true
user=> (contains? "foo" 3)
false
user=> (contains? 'foo 2)
false

It seems to treat strings as it does vectors, seeing if an index is in
bounds or not. It doesn't treat symbols as anything though.

The clojure.contrib.seq-utils/includes? function gives true for "foo" and
\o, so it seems to recognize strings as collections (probably because
they're seqable).

On the matter of efficiency vs. error-catching, perhaps one can have one's
cake and eat it too? How about adding a throw-if! macro to the language that
checks, at macroexpansion time, if it's in debug mode and if not emits no
code, but if so emits (if ~cond (throw (new ~exception-class ~message))).
(It looks like a macro that expands to nil emits no code, and also doesn't
screw things up in any way, anyway.)

How to decide "if it's in debug mode" though? Some global binding check? Of
course, there's also the issue that to turn on or off error-checking in
library functions would require recompiling the library.

Perhaps what's really needed is a new special form, one which emits the same
bytecodes as Java asserts when compiled, and thus can be turned on or off at
runtime in the same way, without recompiling anything (only reloading
affected classes).

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Konrad Hinsen

Tiago Antão a écrit :

> Just a question about the consistency of the API:
> When one passes a "strange" (ie, wrong type) object to contains?, say
> (contains? 'blab 'a)
> the result is a false.
> But if  one passes the wrong type to, e.g., even?, like
> (even? 'a)
> The result is:
> java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to
> java.lang.Number (NO_SOURCE_FILE:0)
> 
> This seems a bit inconsistent, in the sense that one would expect the
> same behavior for the same kind of error.

Is it really the same kind of error? I am not sure. In fact, I'd say 
this depends on the precise definitions of the two predicates.

The behaviour of contains? makes sense if you define "a contains b" as 
"a is a collection AND b is an element of a". This definition implies a 
return value of false whenever a is not a collection.

In the same spirit, one could define "x is even" as "x is an integer AND 
x mod 2 = 0", concluding that (even? 'a) should return false because 'a 
is not an integer. But that definition has its weak points as well, e.g. 
that even? and odd? are not complementary.

I don't think it is possible to define a perfectly coherent set of 
definitions of everything in the Clojure API that satisfies 
simultaneously all conditions that one might expect to hold. In the end 
it's a matter of priorities, and Rich's choices for Clojure are mostly 
in the "pragmatic" category: a compromise between principles, 
simplicity, and efficiency.

Konrad.


__ Information provenant d'ESET NOD32 Antivirus, version de la base des 
signatures de virus 4589 (20091109) __

Le message a été vérifié par ESET NOD32 Antivirus.

http://www.eset.com



--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Tiago Antão

On Mon, Nov 9, 2009 at 8:31 PM, Mark Engelberg  wrote:
> I imagine the rationale is efficiency.  Every core function could
> conceivably do a number of runtime checks to make sure that each input
> is the right kind of type, and then Clojure might feel more sluggish.
> So instead, the core functions just worry about what to do for the
> appropriate inputs.  If you pass a bogus input, the consequence
> depends entirely on how that particular function was coded.  It might
> return a spurious result, or it might error.  There are numerous
> examples of this in the Clojure API.


I understand this, but it creates another kind of problem: When
programmers make mistakes (and I, being not a perfect human being,
make lots of mistakes) the system behaves inconsistently in a place
where one would expect consistency (type checking): sometimes it blows
in your face (like even?), sometimes it does that silently (like
contains?).

>From a software engineering perspective is seems a bit dangerous.
Unless of course, programmers are perfect and never make mistakes, for
those kind, this discussion might seem ridiculous.

I, for one, prefer the blow in your face (ie, like even?) design
approach than the silent one. But above all, consistency would be nice
to have.

But I understand the rationale for efficiency. Tough I question if
there is really a rationale of efficiency or this issue was really
never really thought about (considering that Clojure is such a fast
moving target, that would be normal).

Peace,
Tiago

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Kevin Downey

what makes functional programming better is the reduction of state. so
for example, if I decide that the function call out to contains? is
too much overhead in a tight loop, I can just copy and paste the
relevant code without being concerned that I might miss some crucial
piece of state it twiddles somewhere.

The true beauty of functional programming has little directly to do
with modes of failure. The beauty of functional programming is
transparency of code. I would argue that the kinds of errors you see
are an exact result of this transparency, each one is a little X-ray
window into the inner workings of the function.

On Mon, Nov 9, 2009 at 12:49 PM, Mark Engelberg
 wrote:
>
> Here's another way to think about it.
>
> Why is functional programming better than imperative programming?
>
> One common answer to this question is that functional programs are
> easier to debug.  Why?  Because in an imperative program, if one part
> has an error, the error doesn't necessarily manifest in the portion of
> code that has the error.  Instead, a little piece of memory or state
> can be corrupted in one spot, and then much, much later, an error
> happens as a result of this inconsistency.  Thus the need for
> sophisticated debuggers and steppers to identify where the corruption
> happened (since the manifestation of the error doesn't really help you
> know the source of the problem).
>
> However, in a functional program, you have these well-defined pieces
> that reliably return the same output for a given set of inputs, making
> it easier to test individual components, and when something produces
> an error, it's pretty easy to pinpoint the function where things went
> wrong.
>
> Unfortunately, when core functions don't produce errors for invalid
> inputs, then you have a similar problem as with imperative languages.
> It becomes rather easy to write a program where the consequence of an
> error is far removed from the source.
>
> I have been told that Erlang programmers have developed a culture
> where errors are generally not caught or disguised in any way.
> There's sort of a "crash early and crash hard" philosophy, to increase
> the likelihood that a crash will happen in the block of code that is
> causing the problem and not later.
>
> On Mon, Nov 9, 2009 at 12:35 PM, Mark Engelberg
>  wrote:
>> On Mon, Nov 9, 2009 at 12:32 PM, Kevin Downey  wrote:
>>>
>>> the behavior of functions outside of their domain is undefined. I
>>> guess I still don't get it. why would you use a function on something
>>> outside of its domain? do people just pick functions at random to
>>> compose their programs?
>>
>> Of course they don't pick functions at random, but people do make
>> mistakes.  Although I don't mind Clojure's approach, it is reasonable
>> for people to want clear error messages when they use a function
>> improperly.
>>
>
> >
>



-- 
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

Here's another way to think about it.

Why is functional programming better than imperative programming?

One common answer to this question is that functional programs are
easier to debug.  Why?  Because in an imperative program, if one part
has an error, the error doesn't necessarily manifest in the portion of
code that has the error.  Instead, a little piece of memory or state
can be corrupted in one spot, and then much, much later, an error
happens as a result of this inconsistency.  Thus the need for
sophisticated debuggers and steppers to identify where the corruption
happened (since the manifestation of the error doesn't really help you
know the source of the problem).

However, in a functional program, you have these well-defined pieces
that reliably return the same output for a given set of inputs, making
it easier to test individual components, and when something produces
an error, it's pretty easy to pinpoint the function where things went
wrong.

Unfortunately, when core functions don't produce errors for invalid
inputs, then you have a similar problem as with imperative languages.
It becomes rather easy to write a program where the consequence of an
error is far removed from the source.

I have been told that Erlang programmers have developed a culture
where errors are generally not caught or disguised in any way.
There's sort of a "crash early and crash hard" philosophy, to increase
the likelihood that a crash will happen in the block of code that is
causing the problem and not later.

On Mon, Nov 9, 2009 at 12:35 PM, Mark Engelberg
 wrote:
> On Mon, Nov 9, 2009 at 12:32 PM, Kevin Downey  wrote:
>>
>> the behavior of functions outside of their domain is undefined. I
>> guess I still don't get it. why would you use a function on something
>> outside of its domain? do people just pick functions at random to
>> compose their programs?
>
> Of course they don't pick functions at random, but people do make
> mistakes.  Although I don't mind Clojure's approach, it is reasonable
> for people to want clear error messages when they use a function
> improperly.
>

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

On Mon, Nov 9, 2009 at 12:32 PM, Kevin Downey  wrote:
>
> the behavior of functions outside of their domain is undefined. I
> guess I still don't get it. why would you use a function on something
> outside of its domain? do people just pick functions at random to
> compose their programs?

Of course they don't pick functions at random, but people do make
mistakes.  Although I don't mind Clojure's approach, it is reasonable
for people to want clear error messages when they use a function
improperly.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Kevin Downey

the behavior of functions outside of their domain is undefined. I
guess I still don't get it. why would you use a function on something
outside of its domain? do people just pick functions at random to
compose their programs?


2009/11/9 Tiago Antão :
>
> On Mon, Nov 9, 2009 at 8:08 PM, Kevin Downey  wrote:
>>
>> I don't understand, the error message you get is the error that occurred.
>
> Both of them honor their documentation - no doubt. My point is not
> that, my point is that the behavior is different between the 2
> functions for the same kind of issue:
>
> What is the rationale for even? and contains? having different
> behaviors for the exact same error (ie, one throws the other works
> fine and just returns false on a type error)? From a design
> perspective this seems to increase the cognitive load to programmers
> without any (apparent) reason.
>
> One would imagine that both functions should have the same behavior
> for the same kind of error...
>
> >
>



-- 
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

2009/11/9 Tiago Antão :
> What is the rationale for even? and contains? having different
> behaviors for the exact same error (ie, one throws the other works
> fine and just returns false on a type error)? From a design
> perspective this seems to increase the cognitive load to programmers
> without any (apparent) reason.
>

I imagine the rationale is efficiency.  Every core function could
conceivably do a number of runtime checks to make sure that each input
is the right kind of type, and then Clojure might feel more sluggish.
So instead, the core functions just worry about what to do for the
appropriate inputs.  If you pass a bogus input, the consequence
depends entirely on how that particular function was coded.  It might
return a spurious result, or it might error.  There are numerous
examples of this in the Clojure API.

It wouldn't surprise me if there are a number of programmers who would
be turned off by the ease with which one can shoot yourself in the
foot without getting any kind of error message, but in practice I
haven't gotten bit by this yet.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Mark Engelberg

The general philosophy in Clojure seems to be that if you use a
function in a way that is not intended, there's no guarantee about
what might happen.  You might get an error, or you might just get a
strange result.

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Tiago Antão

On Mon, Nov 9, 2009 at 8:08 PM, Kevin Downey  wrote:
>
> I don't understand, the error message you get is the error that occurred.

Both of them honor their documentation - no doubt. My point is not
that, my point is that the behavior is different between the 2
functions for the same kind of issue:

What is the rationale for even? and contains? having different
behaviors for the exact same error (ie, one throws the other works
fine and just returns false on a type error)? From a design
perspective this seems to increase the cognitive load to programmers
without any (apparent) reason.

One would imagine that both functions should have the same behavior
for the same kind of error...

--~--~-~--~~~---~--~~
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: Consistency of the API

2009-11-09 Thread Kevin Downey

I don't understand, the error message you get is the error that occurred.

the docstring from even? says it throws an exception if the argument
is not and integer.

I would hope that anyone that has read the docstring for contains?
would not use (contains? 'foo 'bar), because, uh, that just makes no
sense. I mean that usage does not match what is indicated in the
docstring

2009/11/9 Tiago Antão :
>
> Hi all,
>
> Just a question about the consistency of the API:
> When one passes a "strange" (ie, wrong type) object to contains?, say
> (contains? 'blab 'a)
> the result is a false.
> But if  one passes the wrong type to, e.g., even?, like
> (even? 'a)
> The result is:
> java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to
> java.lang.Number (NO_SOURCE_FILE:0)
>
> This seems a bit inconsistent, in the sense that one would expect the
> same behavior for the same kind of error.
>
> So the question is: is there any thing less obvious here (at least for
> me), or is this a real issue with the API?
>
> Thanks,
> Tiago
>
> --
> "The hottest places in hell are reserved for those who, in times of
> moral crisis, maintain a neutrality." - Dante
>
> >
>



-- 
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

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



Consistency of the API

2009-11-09 Thread Tiago Antão

Hi all,

Just a question about the consistency of the API:
When one passes a "strange" (ie, wrong type) object to contains?, say
(contains? 'blab 'a)
the result is a false.
But if  one passes the wrong type to, e.g., even?, like
(even? 'a)
The result is:
java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to
java.lang.Number (NO_SOURCE_FILE:0)

This seems a bit inconsistent, in the sense that one would expect the
same behavior for the same kind of error.

So the question is: is there any thing less obvious here (at least for
me), or is this a real issue with the API?

Thanks,
Tiago

-- 
"The hottest places in hell are reserved for those who, in times of
moral crisis, maintain a neutrality." - Dante

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