i guess the only thing to do is to learn dutch, and convince a young Guido van Rossum to create a slightly different version of python
python got a lot of things right
before i start, i want to be clear: python is great, and often a good example for a well designed, well maintained, and well run language. python's build tooling is a pain in the ass, sure, but we'll save that for another time.
python's great success is in being a language with low floors, wide walls, and high ceilings. it's relatively easy to get started, there's lots of different things you can do with it as a beginner, and there's almost no limit to the sorts of things you can build, too. as for the design? well, modula-3 was a good influence, and peps are a great idea too.
so yes, i like a lot of things about python, but this list is the opposite.
a list of warts i'd have polished.
less exceptions, please
look, exceptions are one of the reasons python got created, and i can understand the excitement and desire to use them everywhere, but iteration isn't a great use-case. it could have been much simpler:
i = iter(obj)
while i.next():
print(i.value)
(the design is eric meijer's, for what it's worth. from one of his talks that's sneakily about monadic composition)
hopefully i don't need to do much convincing that this is a better design, especially given how little code it takes to use, or even to implement:
class Iterator:
def __init__(self, obj):
self.obj = obj
self.len, self.pos = len(obj), -1
self.value = None
def next(self):
self.pos +=1
if self.pos >= len: return False
self.value = self.obj[pos]
return True
the nicest thing about not using exceptions is that when you write def next(self): you don't have to worry about your code throwing an GeneratorExit or a StopIteration and accidentally exiting the iterator early.
slightly less duck typing too, while we're at it
perhaps python's most obvious wart is that "abc" is also ["a", "b", "c"] as far as any operations are concerned. every so often you'll encounter an api that takes a list as an argument, and then you stare at the single letters in the exception message until you find the line that needs a ("abc",) around it
there's a similar problem in that [] and "" and 0 are all False, too. although a little convenient, this sugar-like behaviour regularly backfires in practice. the problem isn't "when it quacks like a duck" the problem "strings shouldn't quack like lists", "lists shouldn't quack like booleans"
implicit casts are the devil's playthings, and often the downfall of structural typing.
and strings, well, really, really shouldn't be lists.
let me be clear, foo[x] should not work on strings, but you shouldn't have a foo.charAt(n) method, either.
in python 3, if you have an emoji in your string, the entire thing gets converted to utf-32, just so that the language can do foo[x] in a reasonable time. something you shouldn't be doing in the first place.
strings really aren't lists of codepoints, but lists of lists of codepoints. you probably don't want to split the combining characters apart from the letters they're for. strings should be opaque blobs, without a len, and have methods for splitting it apart: foo.codepoints() foo.chars() foo.lines() and of course, foo.split().
dynamic scope would be nice, too
dynamic scope gets a lot of flak, and by all accounts it's a very clumsy way to build programs, but every so often you need contextual information, and without dynamic scoping, you end up with some sort of kludge.
imagine python had dynamic scope for a moment:
$varwould mean 'dynamically scoped variable'- using
$stdininstead of importingsysand usingsys.stdin - overriding variables by passing named arguments
foo($stdin=fh)
there are a number of python global variables, like sys.path or process wide variables like argv or environment variables, that could all be reasonably exposed through dynamically scoped variables, and it would involve far less bullshit every time you needed to use them
decorators shouldn't be functions
decorators should be objects, with a __decorate__ method. @foo(args) calls foo.__decorate__(args, obj), and functions have a __decorate__ method built in that expects empty args.
now? @foo and @foo() are the same thing. a small footgun, to be sure, but decoupling decorators from functions can be taken one step further. we could pass in the containing module or class into decorator, like __decorate__(obj, module, args)
decorators are already useful, but passing in the enclosing class would save an awful lot of boilerplate.
there's more but
i have other things i'd want to fix. using a __sortkey__ method instead of __eq__ or __hash__. more literals being immutable. classes that flatten instead of inherit. a dictionary type that allows for key-value pairs with empty keys, just so i can write f(**args) and not f(*args, **kwargs).
but most of that feels a little too sugary.
the big changes above—dynamic scoping, contextual decorators, strings as opaque blobs, iterators without exceptions—would change python enough that i'd have new problems to boil piss about.
