Sometimes, Javascript > Python
I am a long-time Python programmer, who, several years ago, had to take up Javascript for work. At first, I found Javascript ugly. But then a funny thing happened; Javascript improved.
So much so, that, even though I am now back working in Python, I occasionally miss Javascript. Specifically, I am not talking about old Javascript, but new Javascript - ES2015 and beyond. New Javascript typically requires a baroque system of compilation (just like C++!), so take this with a grain of salt.
I like Python because it’s clean, well thought out and easy to read but Python has its blemishes. I've found that Javascript shines where Python is dull. Here are some of the ways.
Modules
Modules are superficially easy in Python, but below the surface, it’s a world of pain. The reason is that Python modules are symbolically mapped into Python code where the actual location of the modules is inferred from the symbols. When it’s not clear where the modules are found, you’ll have to figure out which of the many rules that Python follows to look for your module. Is it in the system directory, or the current directory, or the site-packages directory, in a cache file, or in byte-code?
In Javascript (specifically Node) one imports modules from a string. This makes all the difference as the string can be read as a true file-name, thereby short-circuiting a world of pain.
In terms of packaging third-party modules, Javascript has made a trade-off of clarity for space by allowing a local node_module
directory to store all your third-party modules. This is terribly space greedy, but you will know for sure which module you will be importing. Compare this to the dizzying options of managing modules in Python with pyenv
and virutalenv
and __pycache__
and .pyc
files. pyenv
actually overwrites your system environment variables to work!
Specifying packaging in Javascript is refreshingly straightforward. You edit package.json
. That is all. No need to decide between requirements.txt
, setup.py
, setup.cfg
, manifest.in
, or pip.lock
(correct answer - you should use it all - hahaha).
Functional programming
Functional programming is much better in Javascript, which makes sense since Javascript was conceived as a bastardised Lisp with Java syntax.
Still it took the latest version of ES2015 to make it work properly, with proper scoping in let
and const,
and the fat arrow () =>
notation. Still, even old Javascript was better as it had proper anonymous functions, rather than the crippled lambda
in Python, which is restricted to a single line function.
I understand why lambda
is crippled. It’s because significant white space in Python makes it exceedingly hard to do multi-line anonymous functions. For instance, if one were to start a multi-line lambda
in a function parameter, it would very hard to indicate the end of a function, which will require a lot of syntactical jiggling (I found this in coffeescript, a python-esque dialect of Javascript). So to avoid this, Python cripples anonymous functions to a single line.
Still, the support for functional programming in standard Javascript is not quite there. Fortunately, I've found that the lodash
library gives me all the functional programming functionality I need (and if you want go further, you could try ramda
), resulting in terse yet readable code. Equivalent libraries in Python are not as nice because lambda
functions in Python are not as flexible as anonymous functions in Javascript.
Curly braces versus significant white-space
Indented space makes anonymous functions difficult in Python. It also poses difficulty for other types of code.
I’ve written a lot of math heavy code with expressions involving lots of interacting terms that really doesn’t fit on one line. To format it in Python, one would have to use the unsightly line continuator. Similarly with LINQ style programming. In Javascript, one simply continues to the next line.
Modern Python, especially with the Black formatter, found a way around line-continuation by using the bracket trick. Wrapping an expression with brackets allows Python to break into multiple lines without the need for the line continuator and to avoid indentation constraints. What this means is that modern Python (using Black), found a loophole that allows Python to escape significant white-space by using brackets like curly braces!
Well that’s a moral win for Javascript.
Objects nicer, with syntactic sugar
With the new syntactic sugar for objects in Javascript, objects are much cleaner than in Python. A constructor is called a constructor,
and not __init__
. Javascript doesn’t require the verboseness of the explicitly declared self
in object methods. I don’t ever remember finding a use of the self
in the method arguments. In Javascript, it’s always this
and that works fine. If there is any ambiguity, one always just assigns the appropriate self
/this
to another variable further up the object hierarchy. The fat arrow notation also provides a nice short-hand to deal with the implicit context of this
in many cases.
There is something wild-and-whacky in Javascript in that every dictionary {}
can be remade into the object. Just assign a function to an object property, use this
in the function, and the dictionary becomes an object. And all the syntactic sugar in dictionaries just makes exploratory object programming in Javascript a lot of fun. Thanks prototypal inheritance!
Strings
Both Python and Javascript recently introduced templating strings, and they’re wonderful!
The difference is that Javascript only has two types of strings - normal and templated strings denoted by the back-tick. Whereas in Python, you have byte strings, strings, unicode strings, triple-quote strings and several templating styles: old-style %
formatter, the .format
method, and the f
-style templating string.
Another difference is that the Javascript back-tick string does both templating and multi-line. In Python, you have to chose one or the other. So it’s a huge parsimony win for Javascript.
Better for GUI
Python started out as a scripting language and evolved into a general purpose programming language, with great math libraries, that can do graphical GUI programming.
In contrast, Javascript started out as a GUI-scripting language in the web-browser and branched out into a general purpose programming language.
The GUI situation in Python is a bit of a hot mess. Python comes with a default crippled GUI in the form of Tkinter. Otherwise, there are two competing C++ native-based GUI libraries that work with Python - QT and WxWidgets. Both are complex beasts that differ subtly between platforms, and sometimes overrides Python’s own internal standard libraries. And now there are all sorts of bridges of Python into the web-browser (e.g. dash), which ends up being Python interfaces into Javascript!
In contrast, Javascript really shines with GUI programming, for it is unashamedly tied to the web-browser. And the web-browser is the most powerful UI framework ever developed. If you want to break out of the web-browser into the desktop, you can now use Electron, which is arguably the most successful approach to cross-platform GUI development ever developed (I understand Electron is bloat-ware but the cross-platform success of Electron is undeniable - Visual Studio Code and Slack client). Personally, I have found that WebGL in Javascript has been the most productive approach to do 3D graphics programming. And Javascript really pushes the boundary in GUI design - look no further than Mike Bostock’s Observable.
Nothing in Python comes close.
Static Typing
When I first came to Python, I was grateful for its lack of static typing, which simplified the making of new programs by omitting a whole bunch of boilerplate, or so I thought. There is a price to be paid. As one’s programs got more complex, the number of typing errors would proliferate. Eventually, I realized that if only a little bit of static typing could be introduced, then, at least for some modules, debugging life would be a whole lot easier. More so, since modern IDE’s can now make use of types to do amazing code-hinting and help lookups. As well, it would simplify code documentation as I found that function doc-strings were often simply describing the types of the arguments and return values.
In both Python and Javascript, there have been an attempt to add static typing to the language. In Python, there are modules such as mypy, and now, the standard library provides typing annotations. Similarly Javascript has the flow library, and now with Typescript a whole new flavour of statically-typed Javascript is available.
The big difference is that Typescript is a proper static typing extension to Javascript which involves compilation. The compiled code has a level of consistency and guarantees that optional type hinting in Python will never have. This means that in the Javascript world, there is a path to a more principled code-base, if one chooses to pay the price.
Async out of the box
I was always told that Python was a good language for web-servers, but then so is Javascript. The biggest difference is that Javascript was conceived from the beginning as an asynchronous language due to its origin in the web-browser. This means that a programmable asynchronous event loop is baked in the Javascript run-time.
In contrast, Python has a global interpreter lock which is synchronous. To get Python to work with asynchronous programming, several different ways have been designed to circumvent the global interpreter lock, from multiprocessing
to threading
, to heavy weight options such as greenlets in gunicorn
or completely re-hosted versions of Python such as tornado
.
In contrast, in Node, the Javascript system run-time always runs as a programmable asynchronous event loop, making liberal use of callbacks in the standard library. This has the trade-off that it’s much harder to get started, but once understood, a typical Node application is asynchronous from the bottom up. And with modern Javascript using promises
, async
and await
, it’s almost even easy to write async code.
This means that Javascript web-servers in Node don’t require the finagling required to get a Python web-server to run asynchronously. Even though Python too has async
and await
, the async module in Python definitely has the feel of third party library, where there are several different ways just to set up the basic event loop. And because Python has crippled anonymous functions, using callbacks adds much more friction that the equivalent code in Javascript.
Conclusion
Look, don’t get me wrong, I was quite happy to come back to Python, after all it’s the source of my bread-and-butter. But what I have been surprised with is the amount of times I think to myself that it was much nicer in Javascript. And that is all I want to say.