== may be taken to mean identity comparison; 'equals' can only mean one
thing.
Nonsense. "Equals" can be taken to mean anything the language designer
chooses, same as "==". There is no language police that enforces The One
True Meaning Of Equals. In fact, there is no one true meaning of equals.
Even my tiny Pocket Oxford dictionary lists five definitions.
It is ironic that the example you give, that of identity, is the standard
definition of equals in mathematics. 2*2 = 4 does not merely say that
"there is a thing, 2*2, which has the same value as a different thing,
4", but that both sides are the same thing. Two times two *is* four. All
numbers are, in some sense, singletons and equality implies identity.
A language designer might choose to define equals as an identity test, or
as a looser "values are the same" test where the value of an object or
variable is context dependent, *regardless* of how they are spelled: = ==
=== "is" "equals" or even "flibbertigibbet" if they wanted to be
whimsical. The design might allow types to define their own sense of
equality.
Triangle.equals(other_triangle) might be defined to treat any two
congruent triangles as equal; set equality could be defined as an
isomorphism relation; string equality could be defined as case-
insensitive, or to ignore leading and trailing whitespace. Regardless of
whether you or I *would* make those choices, we *could* make those
choices regardless of how our language spells the equality test.
Of course 'formally' these symbols are well defined, but so is
brainf*ck
I don't understand your point here.
So is 'how much wood would a woodchucker chuck if a woodchucker could
chuck wood?'. But how often does that concept turn up in your code?
You didn't make a statement about how often modulo turns up in code
(which is actually quite frequently, and possibly more frequently than
regular division), but about the obscurity of the operation. Taking the
remainder is not an obscure operation. The names "modulo" and "modulus"
may be obscure to those who haven't done a lot of mathematics, but the
concept of remainder is not. "How many pieces are left over after
dividing into equal portions" is something which even small children get.
I didnt know one of Python's design goals was backwards compatibility
with C.
Don't be silly. You know full well Python is not backwards compatible
with C, even if they do share some syntactical features.
C is merely one of many languages which have influenced Python, as are
Haskell, ABC, Pascal, Algol 68, Perl (mostly in the sense of "what not to
do" <wink>), Lisp, and probably many others. It merely happens that C's
use of the notation % for the remainder operation likely influenced
Python's choice of the same notation.
I note that the *semantics* of the operation differs in the two
languages, as I understand that the behaviour of % with negative
arguments is left undefined by the C standard, while Python does specify
the behaviour.
Yes, that was a hyperbole; but quite an often used construct, is it not?
It's hard, but not quite impossible, to write useful Python code without
it, so yes.
You cannot; only constructors modelling a sequence or a dict, and only
in that order. Is that rule clear enough?
But why limit yourself to those restrictive rules?
If I want to collect a sequence of arguments into a string, why shouldn't
I be allowed to write this?
def func(parg, str(args)): ...
If I want to sum a collection of arguments, why not write this?
def func(pargs, sum(args)): ...
Isn't that better than this?
def func(pargs, *args):
args = sum(args)
...
But no. I don't mean those examples to be taken seriously: when you
answer to your own satisfaction why they are bad ideas, you may be closer
to understanding why I believe your idea is also a bad idea.
I hope the above clears that up. It is as much about calling functions
as ** is about raising kwargs to the power of.
I don't understand this comment. Nobody has suggested that ** in function
parameter lists is the exponentiation operator.
As for "calling functions", how else do you expect to generate a type if
you don't call the type constructor? One of your early examples was
something like:
def func(parg, attrdict(kwargs)): ...
If you expect kwargs to be an attrdict, which is not a built-in,
presumably you will have needed to have defined attrdict as a type or
function, and this type or function will get called at run time to
collect the kwargs. That is all.
We dont strictly 'need' any language construct. Real men use assembler,
right?
"We're not using assembly" is not a reason to add a feature to a
language. Every feature adds cost to the language:
* harder to implement;
* harder to maintainer;
* larger code base;
* more documentation to be written;
* more tests to be written;
* more for users to learn
etc.
Yes, I know. How is that not a lot more verbose and worse than what I
have proposed in all possible ways?
That *specific* syntax, outside of function declarations, is something
I've often thought might be useful. But if I were to argue its case, I
would allow arbitrary functions, and treat it as syntactic sugar for:
head, *tail = iterable
tail = func(tail) # or possibly func(*tail)
But that's pie-in-the-sky musing. I certainly wouldn't put it in function
parameter lists. Param lists should be declarations, not code. Apart from
the most limited collation of args, code belongs inside the body of the
function, not the param list:
def foo(a, 2*b+1, c): # double the second arg and add 1
head, tail = somestring[0], somestring[1:]
Well yes, splendid; we can do that with lists too since the dawn of
Python. What you are saying here in effect is that you think the
head/tail syntax is superfluous; that youd rather see it eliminated than
generalized.
No.
It is not "head/tail" syntax, but sequence unpacking, and it has been
nicely generalised to allow things like this in Python 3:
a, b, c, d, *lump, x, y z = any_iterable
any_iterable isn't limited to a list, str, or other object which supports
slicing. It can be any object supporting the iteration protocol.
What I'm saying is that there is no need to OVER-generalise this to
specify the type of *lump within the packing operation. If you want lump
to be something other that Python's choice, perform the conversion
yourself.