This seems to be a very neat intro into Julia and a practical use case. I’ve been postponing to learn the language for a while now, but from what I heard/seen it seems to be extremely well designed.
I used it as my CLI calculator, just like the articles title. I used to use Ruby for this, but Julia is more concise but still readable. Concise enough I have written useful applications that can fit in a tweet.
I love the julia language. I would love to use julia as a command line calculator. Unfortunately, starting the interpreter is excruciatingly slow, an order of magnitude slower than octave or python (and several orders of magnitude slower than bc or dc).
One way to alleviate the slowish startup is to use PackageCompiler.jl (discussed at the end of the article https://krasjet.com/rnd.wlk/julia/#packagecompiler.jl). I like that there's more than just the standard calculator operations available at the REPL, but I can see why you wouldn't use it if you don't need it and bc satisfies your needs.
I remember years ago when caching was about to be integrated in the next version and be here in a few weeks. Now it has been half a decade of people complaining about how slow it is for his to keep recompiling the same thing over and over.
I've been using julia for about three years now and from my interactions with devs and the issues on github, caching more code has only been considered in the past year because before then, the priorities were in getting the language stable, to 1.0, and on other lower-hanging performance fruit. "Just cache more" turns out to be a harder problem than it seems at first sight, if by the very nature of the language loading a package can invalidate your caches. It has to be done with much caution, in order to reduce those invalidations as much as possible, which is why it takes time to figure it out.
Yeah, Julia has had some form of precompilation/caching for ages now, but sorting out all invalidations appears to be highly nontrivial [1] and devs have had more fundamental priorities pre-1.0. There have been some substantial improvements to latency in the meanwhile nonetheless, and PackageCompiler.jl is a viable option as well.
Just saying caching is difficult doesn't gel with my experience of what seems like low hanging fruit. I realize a lot of Julia ends up being templates, but when just one line of importing the same plotting package always takes 30 seconds or so, I get suspicious of the idea that all the usability warts are super difficult to solve.
This has always been a huge complaint for Julia and it seems like one of those things that happens to new languages. People are excited, there are a few big rough edges that drive people away, they get ignored or underprioritized for WAY too long, and a giant window of opportunity is missed. I think of D with weeding out garbage collection, go with generics, D with tools and infrastructure, Zig with its refusal to parse carriage returns or tabs, Rust with compilation times, everyone with a lack of an easy road to a GUI, Jai not being released at all...
I think instead of getting lots of areas to a decent stage, languages end up trying to be the best in a single area and placate their hardcore users, when things like C++ succeed by not having huge blindsides and pitfalls, even if it ends up a little rougher in many areas.
Sure, just saying it's not that they haven't tried. If you have some specific ideas for low-hanging fruit that you think the Julia devs have missed though, I'm sure they'd welcome a PR!
edit: also, time-to-first-plot is a lot less than 30 seconds for me these days, even without PackageCompiler. (time-to-second-plot is, as usual, instantaneous)
> Simply loading the REPL takes half a second for me. I juse it all the time as a simple calculator.
Half a second is precisely what is meant by "excruciatingly slow". If I need a simple calculator, I expect to be able to use it to compute intermediary results in a shell loop. If my loop has one thousand iterations, you are spending about 10 minutes just initializing the interpreter. This is absurd. Of course, you can rewrite your whole loop upside down, so that the interpreter is called outside the loop. But this kind of defeats the goal of considering julia to be a general-purpose calculator, orthogonal with its usage patterns.
The strength of using the julia REPL as a "calculator" is not in doing somewhat trivial calculations like dividing two numbers or quickly multiplying two things, and that's fine. That's not a goal of the REPL and julia is indeed overkill for those kinds of things. However, if you're mathematically inclined and sketch out a few matmuls like in the OP article, julia does have the benefit of making those kinds of interactions more bearable from a syntactic point of view. That's where it shines.
And like I've posted above, if you use certain packages frequently and restart the REPL frequently, why not use PackageCompiler to compile your hot paths into your binary, like described in the article (admittedly at the end)? That way, even the first call in a new session will be fast because it has already been compiled. On top of that, the core devs are very much aware that this is a problem and it's actively being worked on.
> I expect to be able to use it to compute intermediary results in a shell loop
You have a very peculiar use case that most other users don't! ...either you write a shell script, or a Julia script. It's preposterous to want everything to "start instantly", you'll only get stuck with the simplest most primitive tools this way. We'd all be writing C, Bash and PHP by this reasoning.
> If I need a simple calculator, I expect to be able to use it to compute intermediary results in a shell loop.
No offense but that must be one of the dumbest ideas I have heard in a long time. The reason you write something in a shell script is because it is portable on all Unix systems. If you add a dependency to a particular programming language like Julia, then that portability is out the windows. You require installing users to install Julia first.
If you require users to install Julia, you might as well ditch the shell script all together. All shell languages suck anyway for anything non-trivial.
I have rewritten plenty of shell scripts to Julia. It is much faster to write. The code is easier to read and understand and it executes way faster than any shell script.
Running a complicated shell script that calls out to Julia 1000 times makes absolutely NO SENSE! It is a completely contrived example with no practical application.
Besides when people say "calculator," I think most reasonable people assume an interactive system for doing calculations, not a glorified math library. Julia offers a great interactive environment for doing calculations.
> No offense but that must be one of the dumbest ideas I have heard in a long time.
Thanks. This is not the first time I've been told that.
> The reason you write something in a shell script is because it is portable on all Unix systems.
This is definitely not my case. I write plenty of shell code at work, but rarely distribute it. It is just for arranging data around, preparing one-off experiments, and so on.
> Running a complicated shell script that calls out to Julia 1000 times makes absolutely NO SENSE!
I agree that this may be a rare use case, but it is legitimate nonetheless. Moreover, the best tools are those that can be used successfully in ways that the creator of the tool would find abhorrent.
> It is a completely contrived example with no practical application.
This was a concrete example actually. I had a shell script that cropped, denoised and registered a collection of a few thousand photos scattered in a directory tree. It was a four-line pipeline that called a few command line tools from imagemagick and gmic to do the image processing work. At one point, I realized that I had to apply an homography to the images (given by a 3x3 matrix that was stored as 9 numbers in a text file) to map them to a different coordinate system. This involved a 3x3 matrix multiplication for each image. I did this multiplication in julia, my favorite calculator. The running time of the whole thing went from 10 to 20 minutes. Doing the "correct" thing and rewriting everything in julia would be extremely painful.
> Besides when people say "calculator," I think most reasonable people assume an interactive system for doing calculations
When you say "CLI Calculator", you can also think about a calculator that you can call from your shell command line. This is the case for example for the classical calculators bc(1) and dc(1), which are non-interactive, and purposefully intended to be called inside loops in a shell script.
This is simply not true. I have a 3 year old mac, and this is doing simple arithmetic (including starting and exiting Julia). 0.14 seconds user time.
$ time ~/Desktop/Julia_Releases/Julia-1.4.app/Contents/Resources/julia/bin/julia -E "1+1"
2
~/Desktop/Julia_Releases/Julia-1.4.app/Contents/Resources/julia/bin/julia -E 0.14s user 0.08s system 65% cpu 0.342 total
In about 0.5 seconds, I can actually do something non-trivial that actually needs non-trivial compilation - like summing the sin of a million random numbers
$ time ~/Desktop/Julia_Releases/Julia-1.4.app/Contents/Resources/julia/bin/julia -E "sum(sin.(rand(1000000)))"
460165.3756259715
~/Desktop/Julia_Releases/Julia-1.4.app/Contents/Resources/julia/bin/julia -E 0.59s user 0.13s system 93% cpu 0.769 total
By half a second I obviously didn't mean 0.5 seconds but rather a very short duration that I didn't bother to measure.
Edit: Now I bothered to measure. You were saying?
~ time julia -E "1+1"
2
julia -E "1+1" 0,59s user 0,14s system 153% cpu 0,476 total
~ time julia -E "sum(sin.(rand(1000000)))"
459807.21199623536
julia -E "sum(sin.(rand(1000000)))" 1,01s user 0,17s system 125% cpu 0,948 total
My main point was that while half a second (~0.5s) is perceptible startup time, ~0.1s is not. Is it a much older system than mine? I have a 2.3GHz Core i5 from about 2017.
I load a few packages in my startup. `--startup=no` makes the difference between about 0.12s and 0.5s on the 1+1 example for me.
This doesn't really bother me, but maybe I should try PackageCompiler or adding `using OhMyREPL, Revise, Cthulhu, BenchmarkTools` to a userimg.jl for when I build from source.
Besides the other suggestions, you can do things like start julia using the --compile=min or -O0 command line flags to reduce / eliminate compilation overhead and make it startup quicker.
The beta version of version 1.5.0 starts up in 0.25 seconds with --compile=min and I believe it's the same on 1.4.0
How long does it take to start on your computer? If I have run it before. It takes less than 0.25 seconds. I use a several years old Mac. On none of my computers do I experience startup times which has any practical relevance to being productive.
The only real problem I have experienced in Julia is using plotting libraries and those you can include in your Julia image with PackageCompiler.
For me on Debian stable running version 1.0 takes about a second or two to start the repl the first time, less than a second on subsequent runs. On Windows 10 running version 1.3, it takes 10s of seconds every time. Also often times on windows it will load but never display anything until I press a key, so that timing is with me spamming the enter key.
Oh, brilliant! This is exactly what I've been looking for. Didn't realize the syntax was so succint. So far I've preferred excel for my multidimensional calc needs but this fits my needs exactly (need to do simple back-of-the-envelope vector calculations time to time).
EDIT: some info about FriCAS: it's implementation is currently based on Common Lisp, on top of which a language called "Spad" (short for Scratchpad) is implemented. Fricas is a fork of the Axiom computer mathematics system, which in turn is a continuation of Scratchpad. EDIT: and, at least with SBCL, it can use GMP for bignum.
I only skimmed the article, but I believe Fricas supports all uses of Julia mentioned in the article, and more, like advanced symbolic integration.
The problem is that you won't know when your results are wrong because of low input precision. And when you do know, things will be a bit more complicated.
It seems like it would be pretty easy to make a REPL mode that did the big conversions automatically. Not sufficient to protect a naive user, but arbitrary precision floating point is never safe.
> arbitrary precision floating point is never safe
Care to expand on that? Are you advocating for interval arithmetic or maybe decimal floating point?
For what it's worth I think that one would rarely need to interactively deal with so much data at once that using, say, 10000 bit binary floating point would be prohibitive. It is difficult to lose the necessary significant digits with so much "safety margin", and one still has space for about a million of such high-precision floating point numbers.
I agree with your assertion that most users could get away with 10,000 bits. My point is only that there's always going to be somebody who still tries to do something that fails, so you have to pick some set of tradeoffs. Yours seems reasonable.
For a "computational math" (ish) language, having FP64 as the default number format seems pretty reasonnable to me. Cases where one needs bignums of course exist, but I think they are less common. Usually if you need arbitrary precision, you know it.
Turns out I wish I could delete my top-level comment now, because I realized that because of Julia's type conversion and promotion semantics a user would actually not get into the problems I worried about.
Since this is a calculator topic, I'd like request your input on which calculators you use.
I use the Emacs calculator (M-x calc) and XCalc (http://www.tordivel.no/xcalc/) as I like to use RPN calculators. The Windows 10 calculator is painful and slow. XCalc has a mini mode, where it will sit as a small, single line without distracting you too much. I like that feature very much.
One thing with Emacs is that I forget the shortcuts for non-frequent calculations that I'd have to go and search for it.
My favorite calculator of all time is units(1). For example, to calculate how long it takes to download a 20 MiB file over a 56 kbit dialup connection:
$ alias units='units --verbose' # in your shell rc
$ units
Currency exchange rates from FloatRates (USD base) on 2019-06-05
3460 units, 109 prefixes, 109 nonlinear units
You have: 56 kibibit/second
You want: hour/20 mebibyte
reciprocal conversion
1 / (56 kibibit/second) = 0.8126984126984126977077949 hour/20 mebibyte
1 / (56 kibibit/second) = (1 / 1.23046875) hour/20 mebibyte
Note that if you are on macOS, you want to install a more recent units via homebrew. The one shipping with the base system is positively ancient.
I use insect almost daily ( https://github.com/sharkdp/insect ). It’s a command line calculator, not sure how well it would integrate with comint on emacs. Unit conversions built-in. I don’t know whether it has an RPN mode.
The other thing I do frequently is fire up a python3 repl for quick hits. I just did it yesterday for a complex mass file rename I couldn’t figure out how to do with shell variable replacement. With python it was easy with glob and os.rename.
Qalculate is the best desktop calculator I've used. It has a command line version, qalc.
Qalculate takes unit-aware calculation to the next level by including an algebraic solver. You don't have to rearrange everything to put your unknown on one side - you can just chuck 'x' into your equality somewhere and it'll work it out. For example, to verify the what-if xkcd assertion that an A10's main gun will propel Randall's car up to 7mph in 3 seconds:
(60/s) × (x s) × (395 grams) × (1010 m/s) = (3500 lbs)(70 mph) ≈ x = 2.0754319 s
2 seconds! Maybe Randall's car is heavier than 3500 lbs. How much heavier?
More like a Dodge Durango. Unless he's accounting for friction.
You see how nice it is to be able to swap around the unknowns in an equality. Other great features are a large number of built-in constants, an astounding variety of built-in functions - it's practically a CAS - and a command history, which is where I pulled the above example from. It also has a plugin for Plasma's krunner, so you can do quick calculations right there in the dropdown. To summarize: Qalculate is the shit.
Qalculate is great, but often I just use GNU bc. You just have to remember (or make some type of alias) to pass it the -l flag for floating point etc., and I also use -q for quiet. There's also dc(1) for RPN.
Python repl in a newly opened terminal session. Which is similar to the posted submission.
During grad school, I went through a period of trying many different GUI based calculators, until I realized what I really wanted was a language's repl.
I kid you not, I use Spotlight on macOS (cmd+space) as my simple calculator if I’m outside the terminal. If I’m inside the terminal I probably have an IPython instance open. For advanced stuff I use Mathematica as mentioned here: https://news.ycombinator.com/item?id=23428991
Not to mention, Spotlight does a /bunch/ of unit conversions. You can even type things like `1km/h to kt` for example. There's quite a few units defined already that I'm usually pleasantly surprised. This also works on iOS.
The one downside is you can't do unit conversions of a mathematical expression, just of a number. (i.e., 2^2 m/s does not work but 4 m/s does).
Whenever I have to do something more complicated I usually use M-x calc or maxima with ezunits.
One good thing about the Emacs calculator is the sheer number of features available. I don't use many, but occasionally use the financial functions, and they are really great. Any suggestions on programs out there with financial functions?
I have all commands I don't remember bound to leader keys. When in calc I have [leader key] m and then which-key pops up everything I have stored.
Other than that, I use scratch in elisp mode and eval. Not as fluent as magnars, but this is a good example of what I mean (towards the end): https://youtu.be/jNa3axo40qM
I have been using python like this for over two decades. In fact these days I almost never write Python programs themselves, not liking the language much at all anymore. But I still habitually fire up Python to do calculations.
I may switch to Julia, though. I really prefer the language. Wish I could use it at work.
I really wish there was a language based on Swift that would include most of the nice language, REPL and library characteristics of Julia (and perhaps few from Dart). That would get really close to a "perfect language" for me.
Overall I like Julia - and use it as a CLI calculator as well - but there are several things that prevent it from being a good general purpose language:
- OOP support is "meh".
- str.parse(Int).abs is more readable than abs(parse(Int, str)). And this is a very simple case, the readability difference is greater in more complex expressions.
- 1-based indexing.
- Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
- There are some more, this is just from the top of my head.
EDIT: A better example of why Julia's syntax is less readable:
“OOP” is an anemic special case of the multiple dispatch that Julia provides. So if you don’t know any better you can program in that style using Julia.
“str.parse(Int).abs is more readable” No, but it makes sense if you also think that OOP makes sense.
Regardless of what is a subset of what, I certainly agree with the OP that OOP feels "anemic" in comparison to the dispatch-based paradigm of Julia.
I can't tell you how many times I've seen disparate packages written by different authors with no explicit effort towards interoperability "just work" together in Julia (e.g., [1]), something I have never seen to a comparable degree in any other language.
Most dynamic OOP languages don't have interfaces either, so that is kind of a poor argument. You cannot compare Julia to something like Java.
You got to compare it to Ruby, Python etc.
Also I have no idea what an interface definition would look like in Julia. A function in Julia isn't bound to one specific type like in an OOP language.
I think interfaces in Julia might go something like
interface AnimalActions
function walk(x::Animal) end
function call(x::Animal, y::Animal) end
function eat(x::Animal, z::Food) end
end
struct Dog <: Animal
name::String
end
AnimalActions.walk(x::Dog) = true
AnimalActions.call(x::Dog, y::Animal) = true
AnimalActions.eat(x::Dog, z::Food) = true
setinterface Dog AnimalActions end
There are a few scenarios in which I miss simple inheritance when I write Julia code, they are few and far between (in real world problems). What fascinates me is how virtually the whole of the programming world decided that inheritance was the way to go and pursued it for 20 years - on the basis of what now looks like zero evidence.
> str.parse(Int).abs is more readable than abs(parse(Int, str)).
Oof, beauty is in the eye of the beholder. I appreciate the regularity of abs(parse(Int, str)) rather than remember what to call as a function, and what must be called as a method of some object. Of course my preference would be for a syntax that has the added benefit of getting rid of extraneous commas:
(abs (parse int str))
> 1-based indexing
Again, beauty is in the eye of the beholder. A lot of relevant math is 1-indexed. Also, I guess Julia supports arbitrary indexing, though I understand that defaults matter.
I guess that's true, from my experience Julia's way is simply less readable. It also make IDE autocomplete much more useful.
> A lot of relevant math is 1-indexed.
Initially I kind of liked it and disagreed with people who didn't. But as I used Julia over time, I regularly encountered situations in which it was really frustrating and enacted a mental tax. The opposite never happens.
> But as I used Julia over time, I regularly encountered situations in which it was really frustrating and enacted a mental tax. The opposite never happens.
The opposite would be true for a large swath of people coming from scientific computing backgrounds if Julia defaulted to 0-based indexes. That said, in my experience people doing scientific computing have a poorly calibrated sense of what constitutes “hard.” I’m sure plenty of breakthroughs born of brilliant and horrifying code were created over sleepless weekends by grad students who didn’t understand that what they wanted to do was a mental tax.
I guess I don’t hear a lot of people doing scientific computing complain about indexing schemes. I prefer 1-based indexes, but 0-based indexes is a small and trivial issue in my list of grievances against, say, Python.
I've encountered a few pieces of math that are more naturally expressed in 0-based indexes and it is pretty easy to define them in Julia using arbitrary indexing.
TaylorSeries.jl is a great example of this. x = x_0 + x_1 t + ... Then indexing into it with x[0] would give the zero-th order Taylor coefficient.
As someone coming from a physical science rather than CS background, I routinely find 0-based indexing to be "frustrating and a mental tax" compared to 1-based
it's not an OO language. It's got a fantastically rich and powerful type system, but the true beauty of it is the way that it allows you to compose functionality. This is having a staggering impact on the evolution of the ecosystem and the progress that Julia libraries are making in creating functionality that now rivals code bases that have been in dev for 10+ years in the Python landscape.
>- str.parse(Int).abs is more readable than abs(parse(Int, str))
I respect your opinion, but mine is different. Perhaps other people find it easier to read asb(parse(Int,str)) as I do?
>- 1-based indexing.
You can change it and choose.
>- Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
I don't know anything about this one! Fascinating!
>I like when the code reads like a sentece, it's just less mental overhead.
But the OOP version is further from a real sentence here. You're taking the absolute value of the string parsed as an integer, not "for the string grabbing the parser to apply the change to an integer to take the absolute value".
I think they're both equally correct, though the UFCS/pipe syntax is mentally easier to follow.
Let's say someone told you (making up random stuff):
"The absolute value of the sum of the mean of the square of two vectors mapped over the product of X and Y."
Try to think about that in your head. By the time you've reached the end of the sentence, there have been so many steps and no context values that you can't even follow.
If you take the opposite though:
"Take the product of X and Y, map them over two vectors, square it, sum that, and then return the absolute value."
(Again, can't guarantee these are semantically equal to each other, but you get the idea)
Then you know "Okay, starting from these things, first I do this, then I do this.." whereas the opposite way, you're given a ton of directions with zero context, and then have to try to apply them all in reverse once you're finally given the starting value.
You don't give directions to someone starting from the destination and working backwards.
(Using a different account because I got a "you're posting too fast" error.)
It's probably even less readable to be honest. And it would mean there are now 2 ways of doing something which makes the language irregular (unless everyone switches to this syntax).
I would just read it as: "Take str, convert it to an int and take the absolute value." It also corresponds to the order of steps as it actually happens.
Another example where the readability difference is clearer:
Interesting. I love Julia specifically because it doesn’t use an OOP approach. At least for the kind of work I do, I have found multiple dispatch to be far more powerful and cleaner to reason about than dispatching on the type of an implicit first argument.
trivially with multiline cursor (and also trivially ninja'd out with multiline cursor) and have full introspection on the evolution of your data through the pipeline. This is impossible in oo languages (well maybe you could do it in a prototyped language) because dispatch of the .inspect method would have to be implemented on each object.
It gives a syntax error if the pipes are on the newline. Is that your gripe?
Or is it that it doesn't automatically partially apply the function, requiring you to use an anonymous function, like `x->reshape(x,(2,2))` instead of as you have it `resize(100,100)` implicitly. I prefer the explicitness.
Yes, by "proper pipes" I mean partial application. (I used to code julia but now I'm mostly living in elixir because that pays the bills) Yes, the explicitness of what you wrote is a marginal gain in preferability from an idealized point of view, but I promise you your eyes and brain will be more tired from that explcit form than if you just learn to read pipelines as passing into the first value (in elixirland we have lambdas that you can pipe into, too. Nobody does this.)
You don't know what you're missing by not having an inspect function. In elixir it's styled "IO.inspect", and it pretty-prints the value as a side-effect, and returns the value unchanged. You can pass a "label" option as your second parameter, and that will add a label to the pretty print. For example, assume I have a module M with the expected functions:
Now, the great thing about that is that you can blat a bunch of IO.inspects simultaneously using multiline cursors. That low friction 1) causes you to actually use it, and 2) cause you to also set your code up so that it's easy to IO.inspect, which typically also means cleaner, and easier to read code. It is IMO the most powerful debugging tool that I've ever used.
That small pipeline does a pretty complex set of actions - deserialize a raw binary to a structured datatype, then call a handler, then process a handler return result. There are four datatypes that run through that pipeline. Nonetheless, I can inject IO.inspects into that with six keystrokes (I have a vscode snippet), run the code, and watch as the data are transformed, and immediately identify what corner case I missed and write a proper test for it.
And yes, it is far less powerful if the pipes can't tolerate the newline. (though you usually can still multicursor on a single line).
Anyways, I'm not saying this to rag on Julia. I'm just saying I think julia can and SHOULD have this, and it would be awesome. Also, elixir needs to learn a thing or two from julia's repl.
no it's not, because a key part of OO is encapsulation. You really do need a functional language with naked, fully introspectable types everywhere, for this to work effectively.
with Underscores.jl. Not exactly `images.first.resize(100, 100).crop(25, 25, 75, 75)` anymore, to point out that this doesn't need every function to prioritise one argument, the pipe feeds into __ in any position. And that .|> applies them to each element.
I used to be a big Swift fan but as I go back and forth I must say Julia is a much better language, and a lot of that is down to syntax.
Swift syntax is really a pain to deal with. I programmed Objective-C for many years, and that syntax makes sense for object-oriented programming. But it is a disaster for functional programming.
Making closures and calling function objects gets really confusing with Swift syntax. Swift methods cannot naturally be passed around like a Julia function.
> str.parse(Int).abs is more readable than abs(parse(Int, str))
The problem is that the Swift approach entirely lacks composability. Julia's `abs` and `parse` can easily be passed around as first class objects. Besides a lot this readability cases can be solved with the `|>` operator like this:
parse(Int, "-12") |> abs
The Julia approach makes it easy to use functions as arguments to higher order functions such as map and broadcast. Using dot (.) is shorthand for broadcast which lets you do stuff like:
Both cases show parsing an array of numbers and taking the absolute value.
> 1-based indexing.
I know many people hate this but I don't get it. I have spent most of my life with 0-based indexing yet this represents no obstacle for me. When I use Julia I just think as if I am writing math. All my math text books are 1-indexed based and that is just something you get used to. In fact after using Julia for a long time, I frankly prefer 1-based indexing.
0-based indexing IMHO is quite nice when you are dealign with inserting numbers into arrays, or with C-style loops. But neither is usually done in Julia.
> Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
That used to bother me as well coming from Swift. But this request actually makes not sense in a dynamic language. What you are basically asking it to have programming constructs to give you type safety at compile time. But Julia is a dynamically typed language, so you will never have this anyway. It if a fools errand.
Julia is built to catch type problems at runtime, not at compile time. Those problems will be caught at runtime in Julia even without `if let` and `guard let`. They don't actually add any value to a language like Julia.
Swift NEEDS these constructs because statically typed languages are quite poor at catching type problems at runtime. They are very dependent on catching type problems at compile time.
> OOP support is "meh".
I kind of view that as a positive. I have spent years doing object-oriented programming but I must say the Julia way just makes a lot more sense. It is so much easier to work with. I find OOP inherently bad for a REPL environment. And REPL environments make me a lot more productive. You can built up functionality in a far more incremental and iterative fashion.
I still quite like Swift, but I feel it is going in the wrong direction. I tell tale sign for me when going back to Swift code these days is being reminded just how complex the language and the tools have gotten. If you do Swift everyday, that is easy to forget, but the whole toolchain is actually kind of complex.
For instance getting a closure setup correctly with appropriate tagging for life time/ownership of data is not easy. The Schizophrenia between trying to be both an object-oriented language and a functional one becomes more and more apparent.
Sometimes I wonder if Apple made the right choice and whether going with something more akin to Objective-Smalltalk would have been better. It would have been a more natural fit with the Objective-C syntax, which feels very wrong in a functional setting.
I use Mathematica as my advanced calculator. It’s a functional language with an extremely vast and consistent mathematical and non-mathematical library, top notch symbolic capabilities (SymPy is just child’s play in comparison), top notch numerical capabilities, and very decent performance. You certainly don’t need to write your own rotation function — well, 2D rotation is trivial, but what if you want 3D rotation around a vector? Mathematica’s got you covered. Got a command line mode too, see MathKernel.
The downside: it’s not free (as in beer or speech).
I know it's a very general question, but can I ask what sort of calculations you perform using Mathematica? Every time it has a new release, I download the demo and play around with it, but I never get as far as using it to actually compute anything that other CLI calculators couldn't do.
I’m a physicist so all kinds of numerical and symbolic calculations, e.g. very complicated integrals, differential equations, integral equations, and so on. I also work on math problems either seriously (way back when I was an undergrad I worked on some number theoretical problems where computational insight is very important) or for fun (think Project Euler style problems).
If you just need to a calculator to do everyday arithmetic or unit conversions, or even simple physics calculations with units, it’s serious overkill... I certainly hardly ever need it when I’m wearing my programmer hat.
I guess to answer is that the boundary between a calculator and a full blown computational environment is very blurry to me.
The typography in this post is beautiful (as well as the content), anybody know where this comes from? Did the author do it by hand or is it something off the shelf?
What struck me was the [ t a b ] stuff as well as the Latex reference, which looks to be embedded img in src, so there must be some sort of preprocessor?
I have a tangential question related to styling (and I agree the styling for this site is wonderful).
When designing your personal/corporate website/blog, how much weight do you attribute to users whose browsers refuse to load remote fonts (by preference or otherwise)?
I ask because I've noticed recently that some websites (notably, many google sites) are unusable, because they depend on remotely-loaded icons, which are replaced with unreadably-stretched alt-text with unintended form factors.
On this blog, for example, the default preformatted-font didn't seem to be monospaced, and the result is that the "julia" name is rendered incorrectly.
All this is meant to ask, do you ever test your designs with browser-default fonts? If not/so, is this conscious? Why?
To be clear, I intend for this to be a survey, not an inquisition. I'm trying to understand this trend better.
For my own answer. At my place of work, in spite of an avowed obsession with accessibility, our designs are regularly sabotaged by browser-default fonts and poorly-rendered alt-text.
It's just a small mistake in the CSS file :) Since Julia requires a ton of Unicode characters, I'm using a different monospace type from usual, so I forgot to put the fall back `monospace` at the end of `font-family`, but it's fixed now.
This is a great article - interesting, well-written and beautifully presented!
A minor quibble in the typography - the use of small caps for every initial letter is slightly catching my eye every time I see it. Perhaps I don't see enough similar typography on the web to make it feel more natural ¯\\_(ツ)_/¯ ...
I've been using Julia as my CLI calculator for a few years now - cool to see this post! While teaching an online Linear Algebra class, I would use Julia to construct examples and quickly solve them. The syntax sugar of all the functional idioms make it very fun and easy to express more complicated ideas. This is a cool introduction- I was not aware (or had forgotten) about function chaining. Really curious about using more of Latexify as well :)
On an unrelated note, (as a mathematician) I have started using Maple a lot more as my analytic computational helper. Never really gave it the time of day in years past - it is quite powerful and intuitive. Funny note: I recently copied and pasted output of a Maple command into my Julia code and it was syntactically correct!
My main calculator is gnu calc which is the calculator built into emails. It tends to hit various ceilings where it can’t go much further but it has lots of nice features:
1. The data I want to work with tends to be in my text editor so it makes sense to have the calculator there too. Macros can be used to fix up formatting to input numbers
2. Speed of input makes it so much nicer to use. If I look at the examples in the article, here is a comparison. Julia:
7 / 4
x = ans + 2 im
exp(x)
A = [...]
A * [...]
[a^2 for a \in A]
Emacs:
7 RET 4 /
(0,2) + s t x (but I probably wouldn’t use a variable)
s r x E (if I used the stack this is just “E”)
[(same but RET or , not SPC)] s t A
s r A [...] *
s r A V M I Q (that’s vector map inverse square root)
These examples make emacs look bad because I’ve replicated saving things to variables it actually I’d just use the stack. I guess in Julia you can press the up arrow. There are other examples like 1 |> exp |> sin is just 1 E S in Calc (and those operations broadcast over vectors automatically, although I think emasculated does the right thing and doesn’t broadcast exp over matrices).
Skipping the massive tangent of non-calculator things about Julia, we move on to a discussion of plotting. There isn’t a Unicode-art plotting mode in emacs because we have had graphical terminals for over 30 years. In emacs you plot with g f and can then modify your plot with other commands. In just about every language you need to call a function called plot so you’re already losing on characters before you’ve even started. If plotting is fast and easy then exploration is easy.
3. The calculator has lots of useful features that fit well together. Plotting is built in. As are unit conversions, a bunch of symbolic algebra (simplification, differentiation, basic integration, curve fitting, solving equations, numeric integration/solutions, and a super nice facility for editing expressions). There’s a bunch of other functions for stats, vectors (both linalg and things like map/reduce), number theory, physics, etc. There are language modes for things like latex output too. Most stuff works on a bunch of number types (bigints, rationals, arbitrary precision floats, infinity, intervals thereof, complex numbers, error forms, symbolic expressions, dates)
4. I want to reiterate the point about speed. The thing I care about being fast is telling the computer what to do. It takes basically 0 time to do most mathematical operations so latency is dominated by input. So even though emacs uses interpreted emacs lisp and doesn’t even use hardware floating point numbers, it still feels faster than Julia with its optimising JIT. If I want to do something with a large amount of data then I can either wait a few seconds or realise that I’ve gone beyond calculator work and use something better suited.
I think Julia is a great language and it has great advantages over emacs for numerical computing. A big one is that the language and type system is much more naturally extensible for this sort of thing. But I don’t feel that it makes a particularly good calculator. The main reason to think that it is a good calculator is that the alternatives (bash/bc/python/matlab) are so bad.
I wonder if it'd be possible to have a blend between this and https://calca.io where the repl output appears (and change dynamically) as specialized comments within a markdown-friendly format.
SageMath takes 3s to load for me (sometimes a lot more if my systems administrator has updated it recently, which seems to happen a lot).
I therefore use Pari/GP for my personal choice. I like the symbolic nature of Pari/GP over Sage or Julia, even though I spend nearly all of my life programming in Julia, and love the language a lot.
However, for computations of trivial things that I want to run really fast, I put them in a function in Julia (so that they are jit-compiled), since nothing will come close to that, by some orders of magnitude.
One thing I miss is RPN-string input. Unlike normal RPN where you load the "stack," for RPN string you can enter
> 2 3 + 4 *
as one line, press enter, and get 20. This lets you take advantage of the no-parens nature of RPN while viewing the expression on one line rather than flipping through the stack. Only Windows PowerToy scientific calculator had this mode, that I know of.
One of my main gripes about matlab and that Julia unfortunately adopted is the dot syntax. Any other symbol would have worked, but the dot is just way too easy to overlook.
I think it is because it was initially developed for people primarily working with matrices.
Most of the things I do is actually based around multidimensional arrays so when I do operations I almost always want element-wise operations. Unfortunately you often don't even realise you do the wrong operation, just the result is wrong. So I have spend lots of time debugging, until I realised I had a missing dot somewhere, and then spending again significant time trying to find where the dot is missing.
I therefore always found the complaint about np.dot(x,y) being too verbose interesting, because I like that, it makes it explicit and is difficult to overlook.
The @. syntax is definetly an improvement. I don't have an issue with the principle of the dot syntax just with the symbol. The examples in your link perfectly illustrate the issue. If this was a line in a long code base maybe even with mixed element-wise and matrix operations it would be very easy to overlook a missing dot. It would be even worse if the constants were floats. It is just such a terrible choice of symbol for this operation, because it is so easy to overlook.
Everyone I know always looks for missing dots if they encounter a some nonstraight forward bug in matlab (I don't know any Julia users).
133 comments
[ 3.8 ms ] story [ 169 ms ] thread[1] https://github.com/JuliaLang/www.julialang.org/pull/794
This has always been a huge complaint for Julia and it seems like one of those things that happens to new languages. People are excited, there are a few big rough edges that drive people away, they get ignored or underprioritized for WAY too long, and a giant window of opportunity is missed. I think of D with weeding out garbage collection, go with generics, D with tools and infrastructure, Zig with its refusal to parse carriage returns or tabs, Rust with compilation times, everyone with a lack of an easy road to a GUI, Jai not being released at all...
I think instead of getting lots of areas to a decent stage, languages end up trying to be the best in a single area and placate their hardcore users, when things like C++ succeed by not having huge blindsides and pitfalls, even if it ends up a little rougher in many areas.
edit: also, time-to-first-plot is a lot less than 30 seconds for me these days, even without PackageCompiler. (time-to-second-plot is, as usual, instantaneous)
what??
Half a second is precisely what is meant by "excruciatingly slow". If I need a simple calculator, I expect to be able to use it to compute intermediary results in a shell loop. If my loop has one thousand iterations, you are spending about 10 minutes just initializing the interpreter. This is absurd. Of course, you can rewrite your whole loop upside down, so that the interpreter is called outside the loop. But this kind of defeats the goal of considering julia to be a general-purpose calculator, orthogonal with its usage patterns.
or hack up a daemon-process thing where you can start it up outside the loop, then call it inside the loop
And like I've posted above, if you use certain packages frequently and restart the REPL frequently, why not use PackageCompiler to compile your hot paths into your binary, like described in the article (admittedly at the end)? That way, even the first call in a new session will be fast because it has already been compiled. On top of that, the core devs are very much aware that this is a problem and it's actively being worked on.
You have a very peculiar use case that most other users don't! ...either you write a shell script, or a Julia script. It's preposterous to want everything to "start instantly", you'll only get stuck with the simplest most primitive tools this way. We'd all be writing C, Bash and PHP by this reasoning.
No offense but that must be one of the dumbest ideas I have heard in a long time. The reason you write something in a shell script is because it is portable on all Unix systems. If you add a dependency to a particular programming language like Julia, then that portability is out the windows. You require installing users to install Julia first.
If you require users to install Julia, you might as well ditch the shell script all together. All shell languages suck anyway for anything non-trivial.
I have rewritten plenty of shell scripts to Julia. It is much faster to write. The code is easier to read and understand and it executes way faster than any shell script.
Running a complicated shell script that calls out to Julia 1000 times makes absolutely NO SENSE! It is a completely contrived example with no practical application.
Besides when people say "calculator," I think most reasonable people assume an interactive system for doing calculations, not a glorified math library. Julia offers a great interactive environment for doing calculations.
Thanks. This is not the first time I've been told that.
> The reason you write something in a shell script is because it is portable on all Unix systems.
This is definitely not my case. I write plenty of shell code at work, but rarely distribute it. It is just for arranging data around, preparing one-off experiments, and so on.
> Running a complicated shell script that calls out to Julia 1000 times makes absolutely NO SENSE!
I agree that this may be a rare use case, but it is legitimate nonetheless. Moreover, the best tools are those that can be used successfully in ways that the creator of the tool would find abhorrent.
> It is a completely contrived example with no practical application.
This was a concrete example actually. I had a shell script that cropped, denoised and registered a collection of a few thousand photos scattered in a directory tree. It was a four-line pipeline that called a few command line tools from imagemagick and gmic to do the image processing work. At one point, I realized that I had to apply an homography to the images (given by a 3x3 matrix that was stored as 9 numbers in a text file) to map them to a different coordinate system. This involved a 3x3 matrix multiplication for each image. I did this multiplication in julia, my favorite calculator. The running time of the whole thing went from 10 to 20 minutes. Doing the "correct" thing and rewriting everything in julia would be extremely painful.
> Besides when people say "calculator," I think most reasonable people assume an interactive system for doing calculations
When you say "CLI Calculator", you can also think about a calculator that you can call from your shell command line. This is the case for example for the classical calculators bc(1) and dc(1), which are non-interactive, and purposefully intended to be called inside loops in a shell script.
Not necessarily: https://docs.julialang.org/en/v1/manual/running-external-pro...
Edit: Now I bothered to measure. You were saying?
My main point was that while half a second (~0.5s) is perceptible startup time, ~0.1s is not. Is it a much older system than mine? I have a 2.3GHz Core i5 from about 2017.
I don't even think 0.5 seconds are that noticeable...
This doesn't really bother me, but maybe I should try PackageCompiler or adding `using OhMyREPL, Revise, Cthulhu, BenchmarkTools` to a userimg.jl for when I build from source.
The beta version of version 1.5.0 starts up in 0.25 seconds with --compile=min and I believe it's the same on 1.4.0
I suppose Snail could be used in a same way (https://github.com/gcv/julia-snail)
The only real problem I have experienced in Julia is using plotting libraries and those you can include in your Julia image with PackageCompiler.
https://en.wikipedia.org/wiki/FriCAS
https://fricas.github.io
https://github.com/fricas/fricas
http://www.euclideanspace.com/prog/scratchpad/fricas/
EDIT: some info about FriCAS: it's implementation is currently based on Common Lisp, on top of which a language called "Spad" (short for Scratchpad) is implemented. Fricas is a fork of the Axiom computer mathematics system, which in turn is a continuation of Scratchpad. EDIT: and, at least with SBCL, it can use GMP for bignum.
I only skimmed the article, but I believe Fricas supports all uses of Julia mentioned in the article, and more, like advanced symbolic integration.
Care to expand on that? Are you advocating for interval arithmetic or maybe decimal floating point?
For what it's worth I think that one would rarely need to interactively deal with so much data at once that using, say, 10000 bit binary floating point would be prohibitive. It is difficult to lose the necessary significant digits with so much "safety margin", and one still has space for about a million of such high-precision floating point numbers.
I use the Emacs calculator (M-x calc) and XCalc (http://www.tordivel.no/xcalc/) as I like to use RPN calculators. The Windows 10 calculator is painful and slow. XCalc has a mini mode, where it will sit as a small, single line without distracting you too much. I like that feature very much.
One thing with Emacs is that I forget the shortcuts for non-frequent calculations that I'd have to go and search for it.
The other thing I do frequently is fire up a python3 repl for quick hits. I just did it yesterday for a complex mass file rename I couldn’t figure out how to do with shell variable replacement. With python it was easy with glob and os.rename.
Qalculate takes unit-aware calculation to the next level by including an algebraic solver. You don't have to rearrange everything to put your unknown on one side - you can just chuck 'x' into your equality somewhere and it'll work it out. For example, to verify the what-if xkcd assertion that an A10's main gun will propel Randall's car up to 7mph in 3 seconds:
(60/s) × (x s) × (395 grams) × (1010 m/s) = (3500 lbs)(70 mph) ≈ x = 2.0754319 s
2 seconds! Maybe Randall's car is heavier than 3500 lbs. How much heavier?
(60/s) × (3 s) × (395 grams) × (1010 m/s) = (x lb)(70 mph) ≈ x = 5059.1879
More like a Dodge Durango. Unless he's accounting for friction.
You see how nice it is to be able to swap around the unknowns in an equality. Other great features are a large number of built-in constants, an astounding variety of built-in functions - it's practically a CAS - and a command history, which is where I pulled the above example from. It also has a plugin for Plasma's krunner, so you can do quick calculations right there in the dropdown. To summarize: Qalculate is the shit.
The non-RPN one that I use the most is SpeedCrunch (http://speedcrunch.org/), Insect (https://github.com/sharkdp/insect/) and Frink (http://frinklang.org/). All have unit conversion too.
During grad school, I went through a period of trying many different GUI based calculators, until I realized what I really wanted was a language's repl.
The one downside is you can't do unit conversions of a mathematical expression, just of a number. (i.e., 2^2 m/s does not work but 4 m/s does).
Whenever I have to do something more complicated I usually use M-x calc or maxima with ezunits.
$ math() { echo "scale=2 ; $" | sed -e "s:x::g" | sed -e "s:,::g" | bc; }
$ math 5,382 x 48,927.3
263326728.6
See Andrew Hyatt's Emacs calculator for Financial calculation tutorial: http://web.archive.org/web/20171218022230/https://plus.googl...
(Link rot, Google Plus, web.archive.org!)
Other than that, I use scratch in elisp mode and eval. Not as fluent as magnars, but this is a good example of what I mean (towards the end): https://youtu.be/jNa3axo40qM
Otherwise I use WolframAlpha a lot.
Same. I use insect — https://github.com/sharkdp/insect
I may switch to Julia, though. I really prefer the language. Wish I could use it at work.
Overall I like Julia - and use it as a CLI calculator as well - but there are several things that prevent it from being a good general purpose language:
- OOP support is "meh".
- str.parse(Int).abs is more readable than abs(parse(Int, str)). And this is a very simple case, the readability difference is greater in more complex expressions.
- 1-based indexing.
- Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
- There are some more, this is just from the top of my head.
EDIT: A better example of why Julia's syntax is less readable:
`images.first.resize(100, 100).crop(25, 25, 75, 75)`
versus
`crop(resize(first(images), 100, 100), 25, 25, 75, 75)`
“str.parse(Int).abs is more readable” No, but it makes sense if you also think that OOP makes sense.
You can index based on anything you want.
I know that I can set indexing to be 0-based. Not a good solution.
I can't tell you how many times I've seen disparate packages written by different authors with no explicit effort towards interoperability "just work" together in Julia (e.g., [1]), something I have never seen to a comparable degree in any other language.
[1] https://www.youtube.com/watch?v=kc9HwsxE1OY
You got to compare it to Ruby, Python etc.
Also I have no idea what an interface definition would look like in Julia. A function in Julia isn't bound to one specific type like in an OOP language.
And boy, am I ever glad it is.
Oof, beauty is in the eye of the beholder. I appreciate the regularity of abs(parse(Int, str)) rather than remember what to call as a function, and what must be called as a method of some object. Of course my preference would be for a syntax that has the added benefit of getting rid of extraneous commas:
(abs (parse int str))
> 1-based indexing
Again, beauty is in the eye of the beholder. A lot of relevant math is 1-indexed. Also, I guess Julia supports arbitrary indexing, though I understand that defaults matter.
I guess that's true, from my experience Julia's way is simply less readable. It also make IDE autocomplete much more useful.
> A lot of relevant math is 1-indexed.
Initially I kind of liked it and disagreed with people who didn't. But as I used Julia over time, I regularly encountered situations in which it was really frustrating and enacted a mental tax. The opposite never happens.
The opposite would be true for a large swath of people coming from scientific computing backgrounds if Julia defaulted to 0-based indexes. That said, in my experience people doing scientific computing have a poorly calibrated sense of what constitutes “hard.” I’m sure plenty of breakthroughs born of brilliant and horrifying code were created over sleepless weekends by grad students who didn’t understand that what they wanted to do was a mental tax.
I guess I don’t hear a lot of people doing scientific computing complain about indexing schemes. I prefer 1-based indexes, but 0-based indexes is a small and trivial issue in my list of grievances against, say, Python.
TaylorSeries.jl is a great example of this. x = x_0 + x_1 t + ... Then indexing into it with x[0] would give the zero-th order Taylor coefficient.
it's not an OO language. It's got a fantastically rich and powerful type system, but the true beauty of it is the way that it allows you to compose functionality. This is having a staggering impact on the evolution of the ecosystem and the progress that Julia libraries are making in creating functionality that now rivals code bases that have been in dev for 10+ years in the Python landscape.
>- str.parse(Int).abs is more readable than abs(parse(Int, str))
I respect your opinion, but mine is different. Perhaps other people find it easier to read asb(parse(Int,str)) as I do?
>- 1-based indexing.
You can change it and choose.
>- Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
I don't know anything about this one! Fascinating!
I know, not sure if it this would make things better or worse overall.
> I respect your opinion, but mine is different. Perhaps other people find it easier to read asb(parse(Int,str)) as I do?
I like when the code reads like a sentece, it's just less mental overhead.
>I like when the code reads like a sentece, it's just less mental overhead.
But the OOP version is further from a real sentence here. You're taking the absolute value of the string parsed as an integer, not "for the string grabbing the parser to apply the change to an integer to take the absolute value".
Let's say someone told you (making up random stuff):
"The absolute value of the sum of the mean of the square of two vectors mapped over the product of X and Y."
Try to think about that in your head. By the time you've reached the end of the sentence, there have been so many steps and no context values that you can't even follow.
If you take the opposite though:
"Take the product of X and Y, map them over two vectors, square it, sum that, and then return the absolute value."
(Again, can't guarantee these are semantically equal to each other, but you get the idea)
Then you know "Okay, starting from these things, first I do this, then I do this.." whereas the opposite way, you're given a ton of directions with zero context, and then have to try to apply them all in reverse once you're finally given the starting value.
You don't give directions to someone starting from the destination and working backwards.
abs(sum(mean(abs2,x .* y)))?
It's probably even less readable to be honest. And it would mean there are now 2 ways of doing something which makes the language irregular (unless everyone switches to this syntax).
I would just read it as: "Take str, convert it to an int and take the absolute value." It also corresponds to the order of steps as it actually happens.
Another example where the readability difference is clearer:
`images.first.resize(100, 100).crop(25, 25, 75, 75)`
versus
`crop(resize(first(images), 100, 100), 25, 25, 75, 75)`
The former one isn't just more readable, it's also easier to write.
Interesting. I love Julia specifically because it doesn’t use an OOP approach. At least for the kind of work I do, I have found multiple dispatch to be far more powerful and cleaner to reason about than dispatching on the type of an implicit first argument.
But multiple dispatch support is great!
It gives a syntax error if the pipes are on the newline. Is that your gripe?
Or is it that it doesn't automatically partially apply the function, requiring you to use an anonymous function, like `x->reshape(x,(2,2))` instead of as you have it `resize(100,100)` implicitly. I prefer the explicitness.
You don't know what you're missing by not having an inspect function. In elixir it's styled "IO.inspect", and it pretty-prints the value as a side-effect, and returns the value unchanged. You can pass a "label" option as your second parameter, and that will add a label to the pretty print. For example, assume I have a module M with the expected functions:
will print: and value will be assigned 50.Now, the great thing about that is that you can blat a bunch of IO.inspects simultaneously using multiline cursors. That low friction 1) causes you to actually use it, and 2) cause you to also set your code up so that it's easy to IO.inspect, which typically also means cleaner, and easier to read code. It is IMO the most powerful debugging tool that I've ever used.
Example: https://github.com/ityonemo/ex_dhcp/blob/master/lib/ex_dhcp....
That small pipeline does a pretty complex set of actions - deserialize a raw binary to a structured datatype, then call a handler, then process a handler return result. There are four datatypes that run through that pipeline. Nonetheless, I can inject IO.inspects into that with six keystrokes (I have a vscode snippet), run the code, and watch as the data are transformed, and immediately identify what corner case I missed and write a proper test for it.
And yes, it is far less powerful if the pipes can't tolerate the newline. (though you usually can still multicursor on a single line).
Anyways, I'm not saying this to rag on Julia. I'm just saying I think julia can and SHOULD have this, and it would be awesome. Also, elixir needs to learn a thing or two from julia's repl.
It's possible in languages which support extensions, such as Swift, Dart or Kotlin.
But maybe I don't understand your point, if so, tell me what exactly would be impossible to implement in a language like Swift, Dart, Kotlin.
Swift syntax is really a pain to deal with. I programmed Objective-C for many years, and that syntax makes sense for object-oriented programming. But it is a disaster for functional programming.
Making closures and calling function objects gets really confusing with Swift syntax. Swift methods cannot naturally be passed around like a Julia function.
> str.parse(Int).abs is more readable than abs(parse(Int, str))
The problem is that the Swift approach entirely lacks composability. Julia's `abs` and `parse` can easily be passed around as first class objects. Besides a lot this readability cases can be solved with the `|>` operator like this:
parse(Int, "-12") |> abs
The Julia approach makes it easy to use functions as arguments to higher order functions such as map and broadcast. Using dot (.) is shorthand for broadcast which lets you do stuff like:
abs.(parse.(Int, ["-12", "-3"])) parse.(Int, ["-12", "-3"]) .|> abs
Both cases show parsing an array of numbers and taking the absolute value.
> 1-based indexing.
I know many people hate this but I don't get it. I have spent most of my life with 0-based indexing yet this represents no obstacle for me. When I use Julia I just think as if I am writing math. All my math text books are 1-indexed based and that is just something you get used to. In fact after using Julia for a long time, I frankly prefer 1-based indexing.
0-based indexing IMHO is quite nice when you are dealign with inserting numbers into arrays, or with C-style loops. But neither is usually done in Julia.
> Lack of good support for nullability, think of "if let", "guard let", "??", "object?.property".
That used to bother me as well coming from Swift. But this request actually makes not sense in a dynamic language. What you are basically asking it to have programming constructs to give you type safety at compile time. But Julia is a dynamically typed language, so you will never have this anyway. It if a fools errand.
Julia is built to catch type problems at runtime, not at compile time. Those problems will be caught at runtime in Julia even without `if let` and `guard let`. They don't actually add any value to a language like Julia.
Swift NEEDS these constructs because statically typed languages are quite poor at catching type problems at runtime. They are very dependent on catching type problems at compile time.
> OOP support is "meh". I kind of view that as a positive. I have spent years doing object-oriented programming but I must say the Julia way just makes a lot more sense. It is so much easier to work with. I find OOP inherently bad for a REPL environment. And REPL environments make me a lot more productive. You can built up functionality in a far more incremental and iterative fashion.
I still quite like Swift, but I feel it is going in the wrong direction. I tell tale sign for me when going back to Swift code these days is being reminded just how complex the language and the tools have gotten. If you do Swift everyday, that is easy to forget, but the whole toolchain is actually kind of complex.
For instance getting a closure setup correctly with appropriate tagging for life time/ownership of data is not easy. The Schizophrenia between trying to be both an object-oriented language and a functional one becomes more and more apparent.
Sometimes I wonder if Apple made the right choice and whether going with something more akin to Objective-Smalltalk would have been better. It would have been a more natural fit with the Objective-C syntax, which feels very wrong in a functional setting.
J in its simplest use acts just like a desk calculator.
If you are working at a terminal, type the following input, pressing the “enter” key after each line :
7 100The downside: it’s not free (as in beer or speech).
If you just need to a calculator to do everyday arithmetic or unit conversions, or even simple physics calculations with units, it’s serious overkill... I certainly hardly ever need it when I’m wearing my programmer hat.
I guess to answer is that the boundary between a calculator and a full blown computational environment is very blurry to me.
- https://www.wolfram.com/raspberry-pi/
- https://blog.wolfram.com/2019/07/11/mathematica-12-available...
Asking for a friend...
What struck me was the [ t a b ] stuff as well as the Latex reference, which looks to be embedded img in src, so there must be some sort of preprocessor?
For more information, see the colophon [1] and karasu [2].
[1]: https://krasjet.com/colophon/
[2]: https://github.com/Krasjet/karasu
When designing your personal/corporate website/blog, how much weight do you attribute to users whose browsers refuse to load remote fonts (by preference or otherwise)?
I ask because I've noticed recently that some websites (notably, many google sites) are unusable, because they depend on remotely-loaded icons, which are replaced with unreadably-stretched alt-text with unintended form factors.
On this blog, for example, the default preformatted-font didn't seem to be monospaced, and the result is that the "julia" name is rendered incorrectly.
All this is meant to ask, do you ever test your designs with browser-default fonts? If not/so, is this conscious? Why?
To be clear, I intend for this to be a survey, not an inquisition. I'm trying to understand this trend better.
For my own answer. At my place of work, in spite of an avowed obsession with accessibility, our designs are regularly sabotaged by browser-default fonts and poorly-rendered alt-text.
A minor quibble in the typography - the use of small caps for every initial letter is slightly catching my eye every time I see it. Perhaps I don't see enough similar typography on the web to make it feel more natural ¯\\_(ツ)_/¯ ...
On an unrelated note, (as a mathematician) I have started using Maple a lot more as my analytic computational helper. Never really gave it the time of day in years past - it is quite powerful and intuitive. Funny note: I recently copied and pasted output of a Maple command into my Julia code and it was syntactically correct!
1. The data I want to work with tends to be in my text editor so it makes sense to have the calculator there too. Macros can be used to fix up formatting to input numbers
2. Speed of input makes it so much nicer to use. If I look at the examples in the article, here is a comparison. Julia:
Emacs: These examples make emacs look bad because I’ve replicated saving things to variables it actually I’d just use the stack. I guess in Julia you can press the up arrow. There are other examples like 1 |> exp |> sin is just 1 E S in Calc (and those operations broadcast over vectors automatically, although I think emasculated does the right thing and doesn’t broadcast exp over matrices).Skipping the massive tangent of non-calculator things about Julia, we move on to a discussion of plotting. There isn’t a Unicode-art plotting mode in emacs because we have had graphical terminals for over 30 years. In emacs you plot with g f and can then modify your plot with other commands. In just about every language you need to call a function called plot so you’re already losing on characters before you’ve even started. If plotting is fast and easy then exploration is easy.
3. The calculator has lots of useful features that fit well together. Plotting is built in. As are unit conversions, a bunch of symbolic algebra (simplification, differentiation, basic integration, curve fitting, solving equations, numeric integration/solutions, and a super nice facility for editing expressions). There’s a bunch of other functions for stats, vectors (both linalg and things like map/reduce), number theory, physics, etc. There are language modes for things like latex output too. Most stuff works on a bunch of number types (bigints, rationals, arbitrary precision floats, infinity, intervals thereof, complex numbers, error forms, symbolic expressions, dates)
4. I want to reiterate the point about speed. The thing I care about being fast is telling the computer what to do. It takes basically 0 time to do most mathematical operations so latency is dominated by input. So even though emacs uses interpreted emacs lisp and doesn’t even use hardware floating point numbers, it still feels faster than Julia with its optimising JIT. If I want to do something with a large amount of data then I can either wait a few seconds or realise that I’ve gone beyond calculator work and use something better suited.
I think Julia is a great language and it has great advantages over emacs for numerical computing. A big one is that the language and type system is much more naturally extensible for this sort of thing. But I don’t feel that it makes a particularly good calculator. The main reason to think that it is a good calculator is that the alternatives (bash/bc/python/matlab) are so bad.
I therefore use Pari/GP for my personal choice. I like the symbolic nature of Pari/GP over Sage or Julia, even though I spend nearly all of my life programming in Julia, and love the language a lot.
However, for computations of trivial things that I want to run really fast, I put them in a function in Julia (so that they are jit-compiled), since nothing will come close to that, by some orders of magnitude.
> 2 3 + 4 *
as one line, press enter, and get 20. This lets you take advantage of the no-parens nature of RPN while viewing the expression on one line rather than flipping through the stack. Only Windows PowerToy scientific calculator had this mode, that I know of.
I think it is because it was initially developed for people primarily working with matrices.
Most of the things I do is actually based around multidimensional arrays so when I do operations I almost always want element-wise operations. Unfortunately you often don't even realise you do the wrong operation, just the result is wrong. So I have spend lots of time debugging, until I realised I had a missing dot somewhere, and then spending again significant time trying to find where the dot is missing.
I therefore always found the complaint about np.dot(x,y) being too verbose interesting, because I like that, it makes it explicit and is difficult to overlook.
I personally like the dot syntax. It's ergonomic, and provides cool stuff like this: https://julialang.org/blog/2017/01/moredots/
I don't know of an equivalent functionality in any other language.
Everyone I know always looks for missing dots if they encounter a some nonstraight forward bug in matlab (I don't know any Julia users).
For multi-dimensional array matrix it provides the fastest computation as well, even compared to Chapel and Julia [2].
[1]https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...
[2]https://dlang.org/blog/2020/06/03/a-look-at-chapel-d-and-jul...