Hacker News new | past | comments | ask | show | jobs | submit login
An introduction to the Julia language, part 1 (lwn.net)
78 points by leephillips 7 months ago | hide | past | web | favorite | 52 comments

Congrats to JuliaLang team on shipping 1.0 release! Adoption is widespread across disciplines, from genomics to cosmology. Am currently evaluating JuliaImages, and it's almost feature complete with matlab. Predict next-gen computer vision framework will be designed in JuliaLang ;)


I’ve been using Julia for a couple of months now and it’s unparalleled for scientific computation. More expressive than say python, super fast, easy ffi with python, cpp, MATLAB, Mathematica and others.

But above all it’s the community that takes the cake. So many hard algorithms that I would have a hard time doing from scratch are implemented in Julia.

>So many hard algorithms that I would have a hard time doing from scratch are implemented in Julia.

Well it helps that it's a lot easier to implement stuff from scratch in Julia than pretty much any other language.

You are correct.

How good is symbolic math in comparison to Mathematica?

Replying to your other comment

>It's surprising to me that there isn't a good way to call Maxima from either Python/R/Julia or even a non-CL lisp.

There's Sage, which integrates Scipy/Numpy, Maxima, and some other things. However, I personally find Maxima to be pretty awful to use, and even it is nowhere close to Mathematica in functionality at this point. Mathematica itself is horrible to use (although extemely capable), so I'd love an open source, good alternative.

A general comment about Mathematica, not just a response to the comment above:

Trying to use Mathematica like Python/Matlab is setting yourself up for failure. It took me a few months of getting frustrated with Mathematica till I suddenly "got" it [1]. But once I understood that it's programming model revolves around pattern matching and string replacement, things just clicked and it was very easy to write Mathematica code after that.

[1] Coincidentally, I was reading a little bit about functional programming when Mathematica finally started making sense. In particular, IIRC, I was looking at "Learn You a Haskell" for fun, unrelated to Mathematica, which was for work.

The term-rewriting paradigm of Mathematica is very cool, kind of like lisp rotated a few degrees. I'd really like to see a more mainstream language adopt that approach. My complaints are more about everything else. Insane scoping rules and constructs (like variables shared between notebooks), and how it is difficult to productively use the language outside of the GUI, but yet the text editor is beyond awful (can't undo more than once, no redo). That and the cells and such drive me crazy.

I'm no expert, but I've been forced to use it on a few occasions, and I've hated every minute of it. I'm fairly fluent in several of languages in different paradigms, but the frustration I experience with Mathematica is worse than any of them.

As leephilips said, there's a package, but it's nowhere near what Mathematica can do symbolically. Julia was more intended as a numerical Matlab replacement.

However, it could have a lot of potential here. It is a fully homoiconic language with real macros, so it could be a very good fit for symbolic math, just as Maxima is built in Common Lisp.

It would be great if there was a real effort towards that. Mathematica is still really the only game in town.

It's surprising to me that there isn't a good way to call Maxima from either Python/R/Julia or even a non-CL lisp.

Since Maxima runs on top of Common Lisp, could it run on a JVM-based Common Lisp, and exchange objects with Clojure?

Could the CLASP project, CL on the LLVM, lead to a Julia-Maxima bridge?

Any other integration ideas from the Common Lisp folks?

The project to watch here is http://nemocas.org/, which is built on top of Julia, but julia itself is not a CAS.

It's not a symbolic math program, although there is a symbolic math package for it, that I haven't tried.

I’ve been really excited about Julia for awhile now.

Does anyone here happen to know why the decision was made to not allow “normal” classes in Julia? I know you can define your own types to get some of the functionality but sometimes in numerical computing with matlab or python it really makes sense for me to pack functionality into a class.

I know in matlab using classes causes a performance hit but I always thought that wasn’t because the feature was bolted on afterwards. Is there something I’m missing here?

> it really makes sense for me to pack functionality into a class.

Why? Where you would ordinarily write

    class Foo {
       int x;

       int my_method() { ... }
can't you just equivalently write

    type Foo

    my_method(foo::Foo) = begin ... end


Sure, I guess my question is WHY do it this way, which (to me) coming from C++/python seems pretty foreign. I was hoping one of the contributors to the language might be lurking here and offer some insight. I found a nice discussion about it here [1] though, if you are interested.


Classes in C++ and Python are essentially types bundled with a bunch of methods associated with that type. In a multiple dispatch [1] language like Julia, methods are not associated only with the first argument—they "belong to" all of the arguments. This is because which method is called depends on the types of all of the arguments, not just the first one, so it no longer makes sense to associate those methods only with the type of the first argument. Therefore it also no longer makes sense for method definitions to live "inside of" the type—aka "class"—of the first argument. Therefore, you don't have classes as bundles of types with their methods anymore—because the methods don't belong to the types. Instead, they belong to "generic function" objects, which are a first class abstraction for what in a class-based language is just a collection of methods that happen to have the same name (and may or may not actually be conceptually related). In short, you could shoehorn something that looks like a class into Julia, but it wouldn’t really make any sense.

To answer the next-level question of "why multiple dispatch?"... Multiple dispatch is a generalization of single dispatch and leads to much more composable, extensible systems, largely by providing a simple, intuitive and efficient solution to the somewhat esoteric-sounding “Expression Problem” [2]. Even though the problem sounds obscure, it has profound practical consequences and most of the stumbling blocks to writing reusable, composable code in both object oriented and functional languages boil down to it being hard to either extend existing operations to new types or to define new operations on existing types. Julia does both so easily that it’s easy to forget that it’s a problem in other languages. Multiple dispatch is also a particularly good fit for numerical programming problems where it often makes little sense for a specialized operation to "belong" only to its first argument. For example, `x + y` doesn't belong to `x` any more than it does to `y`.

[1] https://en.wikipedia.org/wiki/Multiple_dispatch

[2] https://en.wikipedia.org/wiki/Expression_problem

Julia's atom of compilation is the function, so having as few unique function definitions as possible makes it easier to optimize and reduces compilation time.

Encapsulation. It doesn't look like you can separate private and public variables there.

As someone who’s written a lot of Python I understand where you’re coming from, but I generally think of it the other way around: why does Python lack the ability to create lightweight structs that a lot of other languages have and I often find exceedingly useful (I mean I know why, dynamics and duck typing and all that).

Creating a whole class just to neatly carry around a few related values always felt like a very “heavy” solution.

The Wikipedia page on Julia says one of the original goals was that Julia could be used as a specification language. Does Julia already provide tools for formal verification?

That seems likely to be an unfounded claim since that wasn't one of the original goals.

I recall reading that the biggest problem with Julia was that the language was not sufficiently well tested in an automated way. The result of which was that complex bugs kept creeping back in making the language less reliable in a production environment.

Is my memory faulty? If not, have these concerns been addressed?

You're probably thinking of complaints that our CI was flakey at times (failing despite changes being ok), which is true (though it's been pretty stable lately). However, people sometimes equate that with bad test coverage, which is absolutely not the same thing. Easiest way to get a test to pass is to delete it.

From my perspective at least, the julia test suite is quite comprehensive (if something passes the test suite, I'm generally happy to put it on master and have people use it). We're also lucky that there is so much open source julia code available, which we can use as essentially one giant regression test. We used that heavily in the lead up to 1.0 to make sure that there weren't any surprise bugs waiting for people.

What is the language lineage? Is it functional or imperative? Dynamic or statically typed? Garbage collected or not? I wish every introduction to a new language would contain that kind of info, it'd be so much more informative.

Edit: missed one, natively compiled or VM based.

Julia is an attempt at the best of all worlds:

- It is functional (including lisp-like macro programming) but has a strict type system along with multiple dispatch, which effectively allows for OOP constructs.

- It allows for dynamic typing but since it is JIT compiled using LLVM, you can specify static types for variables and thus take advantage of lots of smart optimisation.

- There is garbage collection but also the ability to get right in there and reach into pointers and memory-allocation manually.

It's truly a pleasure to work with once you appreciate what's possible.

There are no reliability or uptime guarantees though. I wouldn't write a serious Web framework in it (though people have certainly tried)

I’ll have a stab at answering this, someone who knows better can correct me.

Dynamic with optional typing. General consensus is that it has a pretty great type system. Union types, language level implementation of high performance missing types. Garbage collected. JIT compiled using LLVM backend; entire language is written in itself, so the compiler doesn’t have any “black boxes”, I.e. it can optimise everything. Great multiple dispatch system. Imperative technically I guess? But feels like it’s lifted a bunch of good ideas from various functional and domain specific languages.

Anything I missed?

It's strongly typed. At the code level, it's dynamic, but you can strictly type your functions. For the types of workloads julia is best at (computational, batch) if you get a type error panic mid-computation, you've done something wrong, and it should be rare.

JIT is better described as "extremely lazy ahead of time compilation" (not my words). Except for globally-scoped commands, e.g. REPL (I think?) code is always going to be compiled before it is run.

Some examples of fantastic things I've done with julia:

1) wrote a drop-in replacement to IEEE floating point and evaluated numerical performance in operations like FFT, linear algebra, machine learning... I only had to write the basic operations + - * /, and a few algebraic functions like one(T). Everything else (matrix mult, linear solving, complex numbers) came for free.

2) wrote a Galois field type and ran experiments on Reed-Solomon erasure coding. Didn't have to rewrite the linear solver \ function. The builtin one worked just fine - well, in version 0.5 I had to patch it, but it worked great in 0.6 and beyond.

3) wrote a DSL that would "write verilog for me". I could pass an integer type and validate easily that the wires had the result I expected, then use the multiple dispatch on a "semantic type" which was a wrapper on String, which literally generated verilog instead of doing operations on numbers. Then I used verilator (an open source verilog -> C transpiler) to dynamically generate the verilog into a .so file, upload it back to julia, and then run full set of unit tests against both. This suite took me a week to write.

User defined types are __no__ different than "native" types.

You are talking to the llvm compiler, sure you are talking in Julia most of the time, but if you happen to want to talk in C or FORTRAN or ASM in the middle of your high level script, just do. No marshalling of I/O or data structure impedance it all compiles the same.

Exactly. Typing choices are a key part of what makes Julia interesting. And user types are first-class.

So can I define custom operations in llvm ir?

You can write custom code in llvm IR.[0] I must leave it to you to tell me if you can use it as an operator. My guess is yes, but I will not be getting to play any time soon.


Actually, aside from the llvm back end, the parser is in lisp. Not really a black box though.

Though I guess you are talking about libraries?

Yes, I've read it breifly, it's a good description but I always prefer human feedback, with languages, the devil is in the details :)

Looks very interesting, any known drawback, maturity, package available, anything?


* A lot of packages are incompatible with the latest versions of Julia. Though because it has stabilized, this is likely to stop being an issue soon.

* There isn’t any major organization that has adopted it yet, which may impact its long term success in contrast to other new languages like go swift rust etc that have a major sponsor. Garbage collector makes it unsuitable for certain real time uses, which is unfortunate given its otherwise predictable performance characteristics.

* Although there are web frameworks for it, I’m not sure whether they are mature enough for production use.

* Personal pet peeve: no infix operator for integer division. I haven’t looked recently whether there is a package for this. I should probably make my own if not.

> * Personal pet peeve: no infix operator for integer division.

Sure there is

    julia> 10 ÷ 3
type as

``` 10 \div<tab> 3 ```

in any decent julia-capable environment (REPL, editors, notebooks, etc)

Oh wow. I see it was just missing from the docs until May, which was why I hadn't seen it.

The FAA is collaborating with Lincoln Labs to develop a new collision avoidance system using Julia¹. I don't know if that's the kind of thing you had in mind, but it's being used by many companies and universities on various projects, and is the language vehicle in a lot of courses on numerical analysis and related topics.


Well, I was thinking more of an organization using it across the board, preferably one that is prominent enough to result in other copycat users.

(To be clear, I really love the Julia language, and use it quite a bit in personal projects - I'm just hoping that it gets strong adoption, since adoption is what drives the development of miscellaneous libraries and packages).

The federal reserve bank has adopted it (for stress testing models? IIRC), I'd say that's pretty major.

They may have additional projects, but I believe you’re talking about https://github.com/FRBNY-DSGE/DSGE.jl .

The transition to v1.0 for packages is taking some time. The core language is so extensible and allows performant packages such that much of what people expect to be core (an FFT) is in an external package which has not yet been updated for 1.0.

Julia can handle rational numbers, with a special syntax: 3//5 + 2//3 returns 19//15, while 3/5 + 2/3 gets you the floating-point answer 1.2666666666666666.

Why not allow a/b + c/d = (a.d + c.b)/(b.d)? It's what happens when I get my pen and paper out in the real world (along with my tongue sticking out and much perspiration and perhaps a few doodles.) Why does Julia use / to cause one of those weird repeated decimal expansion things to appear but // is needed to keep things pure?

Yes I am partially taking the piss but I'm also interested into the design decision into why // is needed to avoid a type conversion. I note that 1/2 = 0.5 was also a design decision.

A rational number is represented in a fundamentally different way than a floating-point type, or a fixed-point number, or a BigFloat. Julia allows you to define your own types, so if you want to define a type that behaves in the special way you want, you're welcome to do so.

If I write 2/3 on a piece of paper it does not suddenly become 0.666666666666666666666 why does Julia (int al) insist on this? (tongue still in cheek)

Well, yeah, that's because it's a piece of paper, not a calculator. More seriously, you have to be very careful "spooky action at a distance" in programming language design. For example, you might say something like "Well, if the compiler can prove that every constant involved is provable in rational arithmetic, do that, otherwise use floating point arithmetic", which seems like a perfectly sensible thing to do, until you realize that it means that very small changes to the code will have drastic effects on the correctness of your program (if suddenly the compiler is no longer able to prove rational arithmetic is the same answer). We do have that sort of thing happen with performance in Julia (and in many other languages too, of course) - if the compiler can prove it can do something faster, it'll try to, but we try, very, very hard to give everything predictable, sequential execution semantics (i.e. you should be able to take code, run it step by step and generally get the same answer as if you had executed it all at once). One particular example of this is comprehensions. In early versions of julia, the type of `[f(x) for x in a]` would depend on what the compiler was able to prove about `f` (and some other things), e.g. if the compiler knew that `f` always returned an `Int`, you'd get a `Vector{Int}`, otherwise a `Vector{Any}` (or whatever the compiler could prove). This turned out to be hugely annoying to people because the semantics of the computation changed (slightly, only an eltype which you generally don't look at), depending on what the compiler was or was not able to prove about your program. These days, we have runtime semantics for non-empty comprehensions and only use static information to improve performance (which as I said before, we do anyway, so users are used to it). That's not to say that you couldn't do a language that changes semantics based on whole program analysis (you could probably do it in Haskell for example), but you'd lose part of the gradual transition from REPL to full program, which is very important to a system like Julia.

If, for some incomprehensible reason, you are not an LWN subscriber, here is a free link:


Please consider subscribing, so they can pay me and other authors for more articles.

Thanks! We've updated the link so users can get there right away.

> Subscription required. The page you have tried to view (An introduction to the Julia language, part 1) is currently available to LWN subscribers only. Reader subscriptions are a necessary way to fund the continued existence of LWN and the quality of its content. If you are already an LWN.net subscriber, please log in with the form below to read this content.

I'm pretty sure I can find a tutorial somewhere else.

Articles on LWN are publicly available the week after publishing.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact