To say that Julia shines is praise without much context. Certainly the code you list could be trivially transcribed into ANSI Common Lisp (dating back to 1984, with earlier precursors) and though I am mostly unschooled in Python, I assume there are comparable features. Python, I gather, has the disadvantage of being slow (except Cython?), but Common Lisp implementations are generally distributed with compilers built in.
There is a bigger problem with your example, nice as it is. Almost a fallacy. There are several ways to look at it.
(1) You can declare success at a task by modifying the task to something you can succeed at doing. That is, deliberately or accidentally (or humorously) misconstrue the task as something easier.
Here’s an example, from Monty Python, How to Play the Flute
(2) Assuming that finding simple solutions to simple parts of a complicated problem will lead to simple solutions to the difficult parts. (In this case, disregarding the evidence that – historically at least – working on solving those complicated problems has involved many clever people writing hundreds of thousands of lines of code, using tools that were, all-in-all, not necessarily inferior in capability from what you have in Julia.) To be specific, if it could be done easily, why are these programs (Mathematica, Maple, Maxima, Axiom, …) .so large? Is it plausible to believe that they are large because they were not written in Julia? I suspect that for most large projects, typically done for Lisp system building, and certainly for most of the CAS in Lisp, C, C++, … – the first order of business is to build a language support that includes what is needed to make the programmers’ tasks simpler. This could be writing macro definitions, defining structures, setting up generic definitions, writing parsers, etc. So a claim that Julia is somehow superior for writing a CAS lacks the context of “compared to …”. Maple was written in Margay, an extension of C. Axiom was written in SPAD, implemented in Lisp. Maxima has a mixture of methods, but data-directed dispatch was there from the beginning.
My earlier example of interval arithmetic was just chosen to illustrate what happens when you mix too many numeric types and have to choose “the winner”. You haven’t necessarily resolved the winner, even in this example. Is the result an interval with complex polar or complex rectangular elements? Is the result stored in decimal format, or are the results converted to rational numbers?
If arbitrary precision and of varying length, do you choose the longest, the shortest, or perhaps (as done in Maxima) appeal to some global fp precision setting? [ why shortest?? maybe you think there is no point in carrying around extra bits if one of the inputs is already uncertain…]
This example is, however, drawn from a world view where the atomic objects are numbers, and it doesn’t deal with “symbols” – i.e. indeterminates m,n,x,y, or constants like pi, e. Take one of the building blocks that are used here. Do they work to convert (say)
r=3//4 theta= n*Pi//16 to rectangular form?
how about converting to polar form the complex number a+b*i where
a=(x+1)(x-1)-x^2+1
b = sin(nPi)
…
Yes, you were assuming explicit real values, but a CAS does not live on them alone.
Indeed, the value above for “a” is equivalent to the integer 0, but if you don’t have a program capable of recognizing that, your programming tools may not be so robust. That is, “if a==0 then r else s” doesn’t necessarily work.
Again, I’ve rattled on quite a bit; I hope you are taking this as food for thought. Certainly you are free to spend your time on learning about CAS and building a Julia CAS.
RJF