On Jan 24, 5:00 pm, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> On Sat, Jan 24, 2009 at 9:06 AM, Rich Hickey <richhic...@gmail.com> wrote:

> > You gotten tied up, here and elsewhere, conflating sequence with
> > container, and almost all of your problems stem from wanting to say
> > sequence and have it mean container. If you want to put the contents
> > of a (possibly empty) set into a sequential container, use a
> > sequential container - () or []. The sequential? predicate will cover
> > both of those as well as lazy sequences of set contents.
>
> There are a lot of different collections in Clojure (can the words
> collection and container be used interchangeably in Clojure?).  But
> they are all connected in the sense that they all implement a
> list-like view of their contents.

That's not quite true. Sometimes a seq is made available by Clojure
for things not participating in Clojure, e.g. Strings.

> This is IMO one of the coolest
> things about Clojure.  Because most of the core Clojure functions use
> this list-like view to manipulate collections, as soon as you use one
> of these functions, you get back something different.  When I apply
> cons, lazy-cons, first, rest, map, filter, etc. to something like [1 2
> 3], #{1 2 3} or '(1 2 3), I get back something that acts like a list,
> but isn't necessarily a list.  What should I call this general concept
> of what these functions return?  I've been calling this general
> concept a sequence (which seems to be supported by the fact that these
> functions are listed in the sequence-in sequence-out section of the
> docs).

So does Clojure - so let's call them sequences.

> And yes, I also think of these non-specific sequences as
> collections, because regardless of how they are implemented, they act
> like lists (which are containers).

No, they don't, e.g. pop is the 'removal' function for lists:

(pop '(1))
-> ()

(pop (seq [1]))
-> ClassCastException

>  How can I not think of (rest [1 2
> 3]) or (lazy-cons 2 #{3}) as both a sequence, as well as some sort of
> collection of 2 and 3?  That's certainly how they act.
>

You can by avoiding any preconceived notion of what sequence ought to
mean. As similar as two things might be, they are free to be unalike
in some areas. Similar != same.

> So yes, I agree that I'm conflating sequence with container, but I'm
> still looking for a better way to think about and classify these
> inputs and outputs.

Sequence works fine as long as you don't read more into it.

> I'd certainly like to have a better
> classification, because as I've tried to illustrate, when you approach
> things with this viewpoint, the lack of an empty sequence feels a bit
> bizarre - an odd divergence in the way that sequences mirror lists.
> So maybe it all comes down to needing better terminology to discuss
> the inputs and outputs of Clojure's sequence functions...
>

Sequence works for me. They either return an ISeq or nothing (nil).

> When analyzing any function, we need to understand what its domain
> (set of inputs) and range (set of outputs) are.  So what are the
> domain and range of Clojure's core sequence functions?
>
> The domain is the set of things on which (seq x) works.  Is there a
> standard term for this (I've been using the term seq-able)?

Just about anything composite is theoretically "seq-able".

>  Is there
> a built-in predicate to test this important classification?
>

No, and why should there be? I have never had a need for such a thing.
There was so much pressure for these darn predicates and yet they
really serve little purpose but to allow people to write less general
code than they should. There was a time when Strings weren't
"seqable", now they are. I'd much rather leave it at: the set of
things for which seq works is the set of things for which (seq x)
works. I have yet to need (seqable? x)

> The range is (I think) a proper subset of the domain.  The outputs of
> Clojure's sequence functions are all seq-able, but certain things are
> missing from the set of outputs.  You won't ever get back a vector,
> for example, or any empty collection.  Is there a standard term for
> the range of seq?

Right now there isn't, it is implied by the range of rest - nil/ISeq.
I'm currently working on more generalization of this, but it won't
lead to a sentinel empty seq - sequence fns may just return "things
supporting seq/stream". In some ways that will mean the "sequence"
functions simply take/return the broadest possible notion of
collections, which they process sequentially.

> Is there a built-in predicate to test this
> important classification?
>

No, and why should there be? I'd rather it be the most general
possible notion. Right now it is (or (nil? x) (seq? x)), but as I
said, there is a potential future world in which the range would be
just - "things supporting seq and stream". I don't have a name for
that yet.

> > seq? tests if something implements ISeq, no more, no less - it is not
> > the defining predicate of sequence. Is that what you want to promise?
>
> > Or do you want to promise you'll return something on which (seq x)
> > works? It works for nil and () and [], and the user wouldn't look for
> > any one specifically.
>
> > Or do you want to promise that it returns the result of seq. It is
> > this notion that is most commonly meant by sequence in Clojure,
> > whether you like it or not.
>
> I think that the promise I want to make is one that is as consistent
> as possible with Clojure's core sequence functions.
>

Then just say sequence and use nil - there really is no problem or
ambiguity here.

> So: the domain of powerset should be the same as the domain of seq.
> The range of powerset should be a homogeneous sequence whose elements
> all belong to the range of seq.
>

Why should it be homogeneous? What does it add to promise that?

> > What you can't do is say - look, I promised sequence but it's broken
> > because (seq? nil) -> false.
>
> Agreed.  But I think it is reasonable to say - hey, this thing should
> return a homogeneous sequence, but I can't find a label that
> accurately captures the commonality of the elements as well as the
> spirit of the problem.  All the standard Clojure concepts and
> predicates don't quite work, so this is an awkward decision.
>

I think sequence works just fine. Your only problem with sequence was
one of not admitting nil as a member.

> But although I didn't see it at the time of my initial post, it seems
> now that the crux of my problem is that we lack good labels and
> predicates for the domain and range of seq, even though these are
> things we use all the time.

I disagree. People use the sequence functions and expect either nil or
something with a first.

> I was complaining that predicates like
> seq?, sequential?, and list? didn't provide reasonable promises, when
> in fact, these predicates don't match Clojure's internal promises
> either.  It just becomes harder to ignore these details when you deal
> with nested sequences, and you have to figure out exactly what value
> belongs inside.
>

Predicates are inherently weak, as they try to put the world in boxes,
but the world is not clearly separable. The 'cool thing' you noted
about Clojure before is it really downplays the importance of concrete
types.

> Right now, a certain amount of handwaving seems fine.  But as bigger
> projects get written and Clojure, and as people develop automated
> tools like QuickCheck to randomly generate test cases, and test
> contracts, being able to specify these promises clearly starts to
> matter.
>
> I think the concepts I'm looking for would be something like this:
> Domain of seq (called seqable).
> (defn seqable? [s] (or nil (sequential? s)))

This is wrong - maps are not sequential but are "seqable".

> Range of seq (called sequence)
> (defn sequence? [s] (or nil (seq? s)))
>
> So, using this terminology, generalized powerset takes a seqable and
> returns a sequence of sequences.  Does this sound right?
>

Go all the way (and future-proof) - takes a seqable and returns a
seqable of seqables, where seqable means: (seq x) works. Note how the
utility is not in any way reduced, but the specificity has evaporated.
You are chasing something whose value is quite limited, at least in
Clojure.

> But would people be able to remember the distinction between
> similar-sounding seq?, sequential?, seqable?, and sequence?  Probably
> not.
>

I recommend people avoid these predicates as much as possible. But
what they do is clearly defined. I don't intend to add seqable?, and
sequence?, at least as we've discussed here.

> > I'm not arguing against a convention here, and am amenable to your
> > argument about the practicality of nil, which is covered by the
> > promises "something on which (seq x) works" and "the result of seq".
>
> Yes.  Now that I've clarified my thought processes in terms of
> matching the domain and range of seq, I'm all the more confident that
> (nil (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3)) is the "right choice" for
> this particular example, and similar problems.
>

The domain of seq and range of rest. That really is simplest.

> nil has three purposes in
> Clojure: it serves as an empty-like base case (e.g., (empty? nil) is
> true, and (cons 1 nil) is (1)), it serves as a false value, and it
> serves as a Java-like null "nothing" value.  This triple purpose
> causes quite a bit of ambiguity that an empty sequence just doesn't
> have.

I don't disagree. nil-punning might die for other reasons. But for
now, best to follow along with seq/rest.

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