On Sat, Jan 24, 2009 at 9:06 AM, Rich Hickey <richhic...@gmail.com> wrote:
> This is already wrong, right? Don't you mean (set s)? You haven't said
> what (powerset [1 2 3 2 1]) is going to be. subsequences != subsets.

Despite the name "powerset" being inspired by sets, when I posed the
puzzle of generalizing the behavior to sequences, I meant:
(powerset [1 2 1]) to mean something like (nil (1) (2) (1) (1 2) (1 1)
(2 1) (1 2 1))
Because sets are the only things that care about uniqueness, this is
how I'd expect sequences to behave.  I didn't specify this behavior
clearly, and I can see why my open-ended question could cause
confusion.  But I don't consider this to be essential to my main
points, though.

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

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

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)?  Is there
a built-in predicate to test this important classification?

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?  Is there a built-in predicate to test this
important classification?

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

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.

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

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

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

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

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

>> In discussions about generating a
>> sequence of all permutations, all combinations, etc., you inevitably
>> end up with arguments over issues like what (permutations nil) should
>> be (some say (nil), some say (()), and some say nil).  If you have an
>> empty sequence, these issues go away.
>>
>
> Really? Don't you still have a problem with "some say (empty-seq),
> some say empty-seq" - why would that be different?

This is getting away from my main point, but since you asked, I'd like
to respond that no, you wouldn't have this problem with an empty
sequence.  Here's why:
Standard definition of permutations of lists:
The permutations of a list S is a list of all the arrangements of S.
So, the permutations of the empty list is a list of all the
arrangements of the empty list.
The only arrangement of the empty list is the empty list.
Therefore, the permutations of the empty list is the list of the empty list.

This falls straight out of the definition (and also happens to be the
natural base case for a recursive definition).  No ambiguity.  But as
soon as you try to define this for Clojure sequences, you run into
problems.  If you try to mirror the list-based definition, you'd
expect (permutations nil) to yield (nil) (and that's certainly how I'd
code it).  However, it has been said repeatedly that "nil is not the
empty sequence... nil is nothing."  So people can reasonably argue
that
(permutations nil) should be nil because it doesn't even make sense to
try to find the arrangements of "nothing".  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.  To answer the question what does (permutations nil) mean, we
first have to figure out which conception of nil applies here.

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