LangWart: Method congestion from mutate multiplicty

M

Mark Janssen

Perhaps so, but consider how you creates new objects in Python. Very rarely
do you do so with an explicit call to the constructor. For example:

n = 5 # Yes.
# or
n = int("5") # No.

Good, alright, this is the point where the concept of a unified object
model comes into play. (Actually, I'm going to say "unified data
model" (or UDM) from now on so as to avoid the confusion that you
point out below that Python seems to already have a "unified object
model" because everything derives from "Object". The point is
actually rather subtle.)

A unified data model as I define it, specifies a canonical atomic unit
(like the unit integer) and an abstract grouping construct in which
these atomic units can be arranged. By themselves, these two can
construct arbitrary levels of data structure complexity. Add the
ability to apply names to these levels, and you have a complete data
model for a happy programming environment. My work, to give you some
context, involves the invention of a "fractal graph" which is where
the name "unified object model" came from, because a fractal graph, I
argue can represent every part of the physical world at every scale.

(Note that everything in a computer is series of these "atomic" bits
organized by the machine into "words" (merely for sake of the
efficiency that such parallelization affords), yet we have these
human-language constructs such as lists and sets (or records, files,
arrays, etc) where *no such things exist in the computer*. Hence,
the usefulness of considering a unified data model as part of the
computer *science*.)

In your example above, you can verify, for example, that the identity
of 5 and int("5") is the same -- even though you're using a
constructor syntax, you're not *constructing* anything at all, which
you may know already (somewhere in the Python docs, Guido points out
that Python pre-constructs the first 100 or so integers as I recall.)
There is, in addition, an implicit constructor for integers, such that
saying 656565 in python will actually construct the integer as if you
said "int("656565")".

In any case, one never operates or concerns oneself with copies of
atomic elements because they are all the same. It's a subtle
meta-philosophical(?) point, not that different that that which occurs
in the science of physics regarding electrons and protons ("Is there
only one electron?":
http://en.wikipedia.org/wiki/One-electron_universe).
py> import this
[...]
There should be one-- and preferably only one --obvious way to do it.

Yes, that is the reference.
Oh dear. Chris was being sarcastic. I thought that, even if the sarcasm
wasn't obvious, his "Ook. Ook!" at the end should have given it away:

Yes, I was aware of his sarcasm. But I was actually wanting to agree
with the fundamental idea: that one could reduce all data types to 1
atomic unit and 1 grouping construct, and like set theory in
mathematics, derive everything else.
it's
just that in order to do that, python would need a unified object
model and it doesn't have one yet.

I'm not sure what you mean by "unified object model", but I'm pretty sure
that Python has one. Everything is an object, with a single[1] hierarchy of
classes.

Hopefully the elucidation above clears up some of that confusion.

Mark
 
T

Terry Reedy

Yes but the body can be compressed to this single line: "list(iterable).sort()"

That single line now evaluates to None, so that does not work.
I am not proposing that in-place modification return the object.

It seems to me that you are, as that is the only way for
'list(iterable).sort()' to replace 'sorted(iterable)', as you proposed
both originally and above.

The reason sorted(iterable) was added is 'list(iterable).sort()', which
newbies would try, *does not work*. Sorted was added so people would not
have to write

tem = list(iterable)
tem.sort()
<statement using tem>
del tem

as they did previously, and instead could write

<statement using sorted(iterable)>

Reversed was added not only for the same reason, but also to avoid the
temporary list altogether when not actually needed, which it often or
usually is not.
 
C

Chris Angelico

Yes, I was aware of his sarcasm. But I was actually wanting to agree
with the fundamental idea: that one could reduce all data types to 1
atomic unit and 1 grouping construct, and like set theory in
mathematics, derive everything else.

There are many things that work fine in theory, but aren't practical.
You could theoretically rewrite any Python program in Ook (or its
non-G-rated cousin), but that doesn't mean that Ook's data model is
worth working with. You could write a Python-to-Ook compiler, perhaps,
for what that's worth. Proving these things possible may be of
curiosity value, but I wouldn't want to actually _work with_ such a
system.

A while ago I put together a language concep[1]t that, similarly,
started with nothing and let the programmer build from there. It
quickly proved to have one massive fundamental flaw: that two
programs, ostensibly written in the same language, could be utterly
and completely different. It'd be like treating Python and bash
scripts as the same language, given that the shebang at the top makes
them both execute just fine. If you reduce everything to nothing, you
(1) force the programmer to do a lot of unnecessary work, and (2)
allow two different programmers to do that work subtly differently and
thus create incompatible programs. (Python already has a little of
this, in that Py2 and Py3 files aren't guaranteed compatible; but
imagine if every source file were different.)

[1] Posted here if you care. http://rosuav.com/1/?id=683

ChrisA
 
M

Mark Janssen

There are many things that work fine in theory, but aren't practical.
You could theoretically rewrite any Python program in Ook (or its
non-G-rated cousin), but that doesn't mean that Ook's data model is
worth working with.

Ah, but you're conflating a *data model* (which is already composed of
simple theoretical elements (like 1/0)) and a *programming language*,
which is composed of either an implicit or explicit data model
(usually the former) AND a set of transforms that operate on it.
IOW, I'm wanting to take something that is usually just inherited and
historical (and thereby taken for granted), and make it something to
look at. Traditional Data Structures in CompSci goes somewhat towards
this end, but doesn't quite take the idea to its ultimate, and that's
what I'm proposing with a unified data model.

mark
 
R

Rick Johnson

Rick wrote:
And you have missed my point, which is that reversed(), and sorted(), were
not added to the language on a whim, but because they were requested, over
and over and over again.

Well, well, this explains everything!

We don't add features because of logic, or because of consistency, or even because of good sense, we simply add them to appease the masses.

psst: Steven, maybe if the C and Java heads moan long enough we can get braced scoping, or, if the ruby guys pester us enough we can adopt the dollar sign to denote globals, the "@@" for class variables, and the "@" for instance variables! Oh yes! Because being loved is SO much more important than sticking to our philosophy of the Python Zen.
"appended" is called list addition.

newlist = oldlist + [item_to_append]

But where is the consistency?

Yes the syntactic sugar of the "plus sign" is concise, however, the "+" operator does not "pair" intuitively with the "append" method. Even IF you transformed the "+" operator into a named method like "add" (or call the magicmethod "__add__") it still fails to mesh properly with "append", and the two words utterly fail to intuitively establish an "in-place" versus "copy-mutate" relationship. Consider the English definitions of "add" and "append"when applied to sequence objects:

DEFINE "add"
Expand a sequence of "somethings" to include
additional "somethings" -- which may be another
sequence of N somethings. However the exact
location of the new "somethings" is not clearly
intuit-able from the word "add" alone!

DEFINE "append"
Expand a sequence of "somethings" to include an
additional "something". Here the exact location
is can be intuited as: "at the end".
flatten is another often requested, hard to implement correctly, function..
The only reason that Python doesn't have a flatten is that nobody can agree
on precisely what it should do.

Steven, the definition of flatten (as relates to sequences) is very, VERY simple:

Return a new sequence that is the result of reducing
a nested sequence of sequences into a single depth
sequence.
Like map, filter, reduce, etc. flatten is not sensibly implemented as a
mutator method, but as a function.

Only because you, along with a few other high ranking members of this community (including the BDFL himself!) have this /aversion/ to true OOP paradigm.

Can you provide an example of how flatten "as a method" is incorrect versusflatten "as a function" is correct, or will this challenge be silently swept under the rug as so many before?
insert, inserted

"inserted" is called addition, together with list slicing when needed.
newlist = [item_to_insert] + oldlist
newlist = oldlist[0:5] + [item_to_insert] + oldlist[5:


"inserted" is called "addition" by who?

If are implying that "seq.__add__()" employs the semantics of the English word "addition" and seq.insert(arg) is a thin wrapper around "oldlist[0:5]+[item]+oldlist[5:]", then fine. But how will someone intuit those two methods as have a "mutating pairs relationship" from the names alone? Not to mention that calling magic methods is anti-pythonic!
Those are nonsense. None of those are in-place mutator methods.

Well not in the current implementation of Python; but they could be if we wanted them to be. Applying mutations both "in-place" and "to a copy" are very helpful. For example, Ruby supplies both forms for many commonly used operations on arrays: (However i am not a fan of these "mutator pairs")

slice, slice!
sort, sort!
flatten, flatten!
collect, collect!
map, map!
uniq, uniq!
reject, reject!
reverse, reverse!
compact, compact!


Steven, just because you have yet to encounter such usage does not mean theusage is "non-sense"

seq.map(func)
new = seq.mapped(func)

seq.filter(lambda x: x<2)
new = seq.filtered(lambda x: x<2)

Especially reduce, which reduces a list to a single item.

Nice to see you are paying attention! I am sure you already know this, although your wording was clumsy and suggests otherwise, but reduce does NOTHING to the list itself:

py> from operator import add
py> a = [1,2,3]
py> reduce(add, a)
6
py> a
[1, 2, 3]

reduce simply returns the result of the reduction; which is an Integer.

However it is my strong belief that the sum function should not exist when a reduce function does. Why? Because "sum([1,2,3])" is the equivalent of "reduce(operator.add, [1,2,3])"; the latter being significantly explicit and the former being a potential linguistical interpretation nightmare.

Now some might complain about this proposed removal of "sum" because the reduce function requires two arguments whereas the sum only needs one, and I say you're correct, however, this negative attribute is an artifact of a "global function nightmare architecture" and IF the core developers of Pythonwould accept the fine principals of true OOP paradigm, then the call wouldlook like this instead:

seq.reduce(operator.add)

This fine example of OOP style is much easier to grok than the either the sum or reduce global functions could ever wish to be.
Again, "extended" is spelled list addition.

Are you sure you've actually programmed in Python before? You seem awfully
ignorant of language features.

So you are saying that "addition" is a "language feature"(sic). Can you please provide an example of "addition" as a language feature?

py> 'addition' in dir(list)
False
[...]
My point was this: All mutate methods should mutate "in-place",

Well duh. All mutator methods do mutate in-place, otherwise they wouldn'tbe
mutator methods.

So "reversed()" and "sorted()" mutate in-place? Do you realize that these two functions are an extension of the list (or sub-types of list) interface and are in fact mutating a seq object in-place! Maybe not the sequence object you called the function on, but a seq object none-the-less!
Been there, done that, it sucks. That's about a dozen steps backwards to a
worse time in Python development.

Why? Because you have to type this

reversed = list(seq).reverse()

Instead of this:

reversed = reversed(seq)

*rolls-eyes*
 
S

Steven D'Aprano

Mark said:
A unified data model as I define it, specifies a canonical atomic unit
(like the unit integer) and an abstract grouping construct in which
these atomic units can be arranged. By themselves, these two can
construct arbitrary levels of data structure complexity. Add the
ability to apply names to these levels, and you have a complete data
model for a happy programming environment. My work, to give you some
context, involves the invention of a "fractal graph" which is where
the name "unified object model" came from, because a fractal graph, I
argue can represent every part of the physical world at every scale.

How can you breathe *way* up there in space?

http://www.joelonsoftware.com/articles/fog0000000018.html

P.S. not all phenomena are fractal. The elbow joint, for instance, is just a
hinge, and not made of smaller elbow joints made of tinier elbow joints
made of even tinier elbow joints made of ...
 
S

Steven D'Aprano

Rick said:
we can get the iterator for free. If however you want to control the
iteration /without/ being locked into a loop, you can explicitly call:

py> iter(seq)
Or, if python employed /true/ OOP paradigm:

py> Iterator(seq)

Today I learned that the difference between "true" OOP and everything else
is the presence of an initial capital letter.

Thank you Rick for your deep insight.
 
T

Tim Chase

flatten, flattened
Steven, the definition of flatten (as relates to sequences) is
very, VERY simple:

Return a new sequence that is the result of reducing
a nested sequence of sequences into a single depth
sequence.

What should you get if you flatten

[[[1,2],[3,4]],[[5,6],[7,8]]]

Should the result be

[[1,2],[3,4],[5,6],[7,8]]

or

[1,2,3,4,5,6,7,8]

I've needed both cases, depending on the situation.

-tkc
 
R

Rick Johnson

[...]
I have to agree with Rick, I think requiring the user to explicitly
create a new object, which is already a good and widely-used practice,
should be the Only One Way to Do It.

Why should I copy a potentially large data structure just to iterate
over it in reverse order?

That's a good question, and the answer is: "Only a fool would make a copy of ANY data structure only to simply iterate over it; be it forwards or backwards or sideways".
And why on earth would you want to remove
the more efficient ways of doing this?

Well these "ways"(sic) might be more efficient, at this time, because the Python developers have defined them to be. This could be changed if they would drop the aversion to true OOP paradigm.

To make this work properly you would need to optimize the constructor of the sequence object. If the user presents the seq object (say for example a list) with variable that points to an already existing list like:

py> a = [1,2,3]
py> for x in list(a).reverse():
.... do_something

Python will not actually create a copy of the list because that would be foolish! Python would instead ITERATE over the existing object transparently. It's called OPTIMIZING CODE! By doing this we gain many things:

* We don't have these foolish "mutate"<->"copy mutate"
method "pairs" like: "seq.reverse()" and
"seq.reversed()"

* We are writing maintainable code by explicitly calling
"Sequence(seq).mutate()". The intent becomes obvious
even though Python may "optimize" our intentions "behind
the scenes".
There is no guessing. If the object has a __reverse__ method then it
specifically advertises that it knows how to create an iterator that
gives its values in reverse order. Otherwise __len__ and __getitem__
are used.

Really.

And you know that simply from intuiting a seemingly unrelated method? Wow, i'd bet the detectives of many municipalities would love to rent some of your powers. What sort of esoteric rule book are you reading from my friend?
 
R

Rick Johnson

[...]
I have to agree with Rick, I think requiring the user to explicitly
create a new object, which is already a good and widely-used practice,
should be the Only One Way to Do It.

Why should I copy a potentially large data structure just to iterate
over it in reverse order?

That's a good question, and the answer is: "Only a fool would make a copy of ANY data structure only to simply iterate over it; be it forwards or backwards or sideways".
And why on earth would you want to remove
the more efficient ways of doing this?

Well these "ways"(sic) might be more efficient, at this time, because the Python developers have defined them to be. This could be changed if they would drop the aversion to true OOP paradigm.

To make this work properly you would need to optimize the constructor of the sequence object. If the user presents the seq object (say for example a list) with variable that points to an already existing list like:

py> a = [1,2,3]
py> for x in list(a).reverse():
.... do_something

Python will not actually create a copy of the list because that would be foolish! Python would instead ITERATE over the existing object transparently. It's called OPTIMIZING CODE! By doing this we gain many things:

* We don't have these foolish "mutate"<->"copy mutate"
method "pairs" like: "seq.reverse()" and
"seq.reversed()"

* We are writing maintainable code by explicitly calling
"Sequence(seq).mutate()". The intent becomes obvious
even though Python may "optimize" our intentions "behind
the scenes".
There is no guessing. If the object has a __reverse__ method then it
specifically advertises that it knows how to create an iterator that
gives its values in reverse order. Otherwise __len__ and __getitem__
are used.

Really.

And you know that simply from intuiting a seemingly unrelated method? Wow, i'd bet the detectives of many municipalities would love to rent some of your powers. What sort of esoteric rule book are you reading from my friend?
 
S

Steven D'Aprano

Rick said:
Well, well, this explains everything!

We don't add features because of logic, or because of consistency, or even
because of good sense, we simply add them to appease the masses.

They were requested because people kept re-inventing them. They kept
re-inventing them because they are useful functions that make good sense to
have.

"appended" is called list addition.

newlist = oldlist + [item_to_append]

But where is the consistency?

Strings use + for concatenation. Tuples use + for concatenation. Lists use +
for concatenation. Seems pretty consistent to me.

Are you sure you've used Python before?

Yes the syntactic sugar of the "plus sign" is concise, however, the "+"
operator does not "pair" intuitively with the "append" method.

Adding "ed" to a word does not pair intuitively with the method name. You
have to learn it, which in the case of most English speakers you probably
did between the ages of 2 and 6.

Using + to represent concatenation is no less intuitive.


Even IF you
transformed the "+" operator into a named method like "add" (or call the
magic method "__add__") it still fails to mesh properly with "append", and
the two words utterly fail to intuitively establish an "in-place" versus
"copy-mutate" relationship.

So what?


Steven, the definition of flatten (as relates to sequences) is very, VERY
simple:

Return a new sequence that is the result of reducing
a nested sequence of sequences into a single depth
sequence.

Very, VERY simple until you actually implement this function, and discover
that it does too much e.g.

flatten([None, 23, [1, 2, 3], Point(x=2, y=3), ["spam", "ham"]])

=> [None, 23, 1, 2, 3, 2, 3, 's', 'p', 'a', 'm', 'h', 'a', 'm']


So people who have *actually programmed*, instead of just talking about
programming, have discovered that in practice you need to treat some
sequences as primitives that don't get expanded.


Only because you, along with a few other high ranking members of this
community (including the BDFL himself!) have this /aversion/ to true OOP
paradigm.

Can you provide an example of how flatten "as a method" is incorrect
versus flatten "as a function" is correct, or will this challenge be
silently swept under the rug as so many before?

Simple efficiency. The most efficient, and sensible, way to flatten a list
is to create a new list. Trying to flatten a list in place is a dumb
idea -- it is O(n**2), or possibly even worse. Since only an idiot would
write flatten as an in-place method, any mutator version of flatten would
have to use a non-mutator version, then use slicing to over-write itself:

def flatten(self):
new_list = flatten(self)
self[:] = new_list


which is fine if you absolutely have to have an in-place version, but why
bother? The caller can do it themselves:

mylist = flatten(mylist)


[snip]
Well not in the current implementation of Python; but they could be if we
wanted them to be.

Now you're smoking crack. How can *reduce* be an in-place mutator method?

py> mylist = [1, 2, 3, 4, 5]
py> from operator import mul
py> reduce(mul, mylist)
120

I would really like to see your implementation of a reduce method that turns
a list into an int *in-place*.


Applying mutations both "in-place" and "to a copy" are
very helpful.
Exactly.


[snip]
Well duh. All mutator methods do mutate in-place, otherwise they wouldn't
be mutator methods.

So "reversed()" and "sorted()" mutate in-place?

No. They are not mutator methods, because they do not mutate in place.

Why? Because you have to type this

reversed = list(seq).reverse()

Are you sure you've programmed in Python before? That gives reversed = None.
 
S

Steven D'Aprano

Rick said:
[...]
I have to agree with Rick, I think requiring the user to explicitly
create a new object, which is already a good and widely-used practice,
should be the Only One Way to Do It.

Why should I copy a potentially large data structure just to iterate
over it in reverse order?

That's a good question, and the answer is: "Only a fool would make a copy
of ANY data structure only to simply iterate over it; be it forwards or
backwards or sideways".

Aren't you the fool who wants to remove reversed() and have people write:

reversed = list(seq).reverse()


Oh yes, you are the fool. And me the bigger fool for listening to you.

Time for another six months in my killfile, methinks.
 
M

Mark Janssen


Haha, point taken, but I actually have an application for such a
high-level abstraction -- a 3-d web. A unified information model
could take the internet to the next level (much like the Internet
itself did to all the disparate communications networks before).
Instead of the current hypertext and cumbersome attempt at coding
semantic meaning in RDF tags, (re)present the internet content in a
3-d space and let the visual cortex, along with the crowd, make the
relationships. (See pangaia.sourceforge.net)
P.S. not all phenomena are fractal. The elbow joint, for instance, is just a
hinge, and not made of smaller elbow joints made of tinier elbow joints
made of even tinier elbow joints made of ...

Oh, no doubt about that. Perhaps instead of fractal graph, I should
call it a recursive graph -- there must be a "base case" which ends
the infinite regress. For the model I've been working on, that base
case is the unit integer (or the machine word with only the
least-significant-bit set to "1").

Cheers,

mark
 
R

Rick Johnson

What should you get if you flatten

[[[1,2],[3,4]],[[5,6],[7,8]]]

Should the result be

[[1,2],[3,4],[5,6],[7,8]]

or

[1,2,3,4,5,6,7,8]

I've needed both cases, depending on the situation.

Well providing /every/ possible solution for /every/ possible answer to /every/ problem is not going to be possible unless you are willing to endure an endless amount of complexity.

My opinion is that flatten should should call seq.flatten() on all sub-sequences. That seems like the only /reasonable/ resolution to allow. At least sub-types could define how they get flattened.

However, that does not solve your problem: where you wish to flatten a sequence down to a prescribed sub-depth; in your example: flatten(subdepth=1)..

class Sequence():
"""Hypothetical sequence object."""
def flatten(self, depth=INFINITY):
# ...

py> seq = [[[1,2],[3,4]],0,[[5,6],[7,8]]]
py> seq.flatten()
[1,2,3,4,0,5,6,7,8]
py> seq.flatten(depth=1)
[[1,2,3,4],0,[5,6,7,8]]
py> seq.flatten(depth=2)
[1,2,3,4,0,5,6,7,8]
py> seq.flatten(depth=3)
# Throw error or just quietly return flat list???

I don't feel very good about this API though. But i admit it might be beneficial to some folks. Should this example be the built-in behavior of Sequence#flatten, probably not. But hey, here at pydev we add features that appease the masses because we want to be loved. So folks, get your votes in! :)
 
R

Rick Johnson

What should you get if you flatten

[[[1,2],[3,4]],[[5,6],[7,8]]]

Should the result be

[[1,2],[3,4],[5,6],[7,8]]

or

[1,2,3,4,5,6,7,8]

I've needed both cases, depending on the situation.

Well providing /every/ possible solution for /every/ possible answer to /every/ problem is not going to be possible unless you are willing to endure an endless amount of complexity.

My opinion is that flatten should should call seq.flatten() on all sub-sequences. That seems like the only /reasonable/ resolution to allow. At least sub-types could define how they get flattened.

However, that does not solve your problem: where you wish to flatten a sequence down to a prescribed sub-depth; in your example: flatten(subdepth=1)..

class Sequence():
"""Hypothetical sequence object."""
def flatten(self, depth=INFINITY):
# ...

py> seq = [[[1,2],[3,4]],0,[[5,6],[7,8]]]
py> seq.flatten()
[1,2,3,4,0,5,6,7,8]
py> seq.flatten(depth=1)
[[1,2,3,4],0,[5,6,7,8]]
py> seq.flatten(depth=2)
[1,2,3,4,0,5,6,7,8]
py> seq.flatten(depth=3)
# Throw error or just quietly return flat list???

I don't feel very good about this API though. But i admit it might be beneficial to some folks. Should this example be the built-in behavior of Sequence#flatten, probably not. But hey, here at pydev we add features that appease the masses because we want to be loved. So folks, get your votes in! :)
 
A

alex23

What Rick means: "I want to claim that I've learned a new language,
but I want it to work exactly like the imaginary language in my mind,
and if it doesn't, I'm going to complain about it, rather than,
yaknow, actually learn a new language."

Yeah, this is, pardon the french, just batshit crazy. How does one
_ever_ learn _anything_ new if they expect everything to conform with
their pre-established intuitions?

As can be seen by his posts, the outcome is one just _doesn't_ learn.
 
M

MRAB

I remember a time when he was saying that the developers of the
language were ignoring the silent majority. Now he's saying that we
shouldn't simply add things to appease the masses.
They were requested because people kept re-inventing them. They kept
re-inventing them because they are useful functions that make good sense to
have.
Exactly.

Practicality beats purity, and all that.
 
A

alex23

We don't add features because of logic, or because of consistency,
or even because of good sense, we simply add them to appease the masses.

When "logic" or "good sense" are writing programs, maybe then we'll
listen more to what they want in a language.

"Good sense" would dictate that someone actually read documentation
when dealing with a new programming language. "Logic" would indicate
that expecting things outside of your control to confirm with your
intuition is a fool's game; I highly recommend not reading up on any
modern physics as there'll be plenty there that just makes you angry.

PS pragmatism is a perfectly valid philosophical approach too. Far
more practical than more "pure" approaches, and something something
foolish consistency blah blah small minds.
 
M

Mark Lawrence

I highly recommend not reading up on any
modern physics as there'll be plenty there that just makes you angry.

Spoil sport. Fancy not wanting rr's views on string theory :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top