Clearing up common beginner misconceptions

To preface, this is just one person’s attempt to help people save some time and engage with a new language with more reasonable expectations, not a standalone or authoritative source of information. I expect learning about Julia or any mentioned topics to be mostly accomplished by studying other sources, and I just hope that my language here is precise enough to provide some helpful search terms. If a reader can clear up some misconceptions by skimming the headers or only reading a few sections, that’s fine too. In no particular order:

Misconception: Julia is the last programming language, the one that will replace all others
Reality: Julia offers an optimizable dynamic language and seeks to work alongside others

To be fair, this myth didn’t come out of nowhere. The first official blogpost in 2012 described the creators as “greedy” for features found in various programming languages, and the 2012 paper proposed Julia as an alternative to “two-tiered architectures” in technical computing with “high-level logic in a dynamic language while the heavy lifting is done in C and Fortran”, which was soon called the “two-language problem”. However, the blogpost didn’t list every language and their features, which would be futile because many features and paradigms are incompatible. The 2012 paper also declared calling C and Fortran routines as a core feature, so despite aiming to reduce the need to wrap those languages, Julia was also designed to do so. Likewise, Julia users embrace great work done in other languages, so the ecosystem does not shy away from wrapping binaries and interoperability. To return the favor in the former way, the experimental JuliaC aims to produce binaries of a manageable size.

Like most programming languages, Julia is an opinion on how to program, specifically in an optimizable dynamic language, and it’s entirely valid to have different opinions and needs that are better served by other languages, for now or forever. For example, Julia is fairly permissive of side effects, so referential transparency and its perks are far more feasible in pure (or more dedicated) functional programming languages. As far as I know, there is no language similar enough to Julia to avoid learning and adjusting to different tradeoffs in a language transition. There are also many good reasons to use a language besides its base design. Perhaps your colleagues or field communicate and collaborate with certain languages, and many great (and well-funded) developers maintain a particular library in one. People can have any kind or number of reasons to invest their time in any practical language, so the most general advice I can give is to consider your needs and keep an open mind.

Misconception: Julia’s compiler is an easy guarantee of peak performance
Reality: Optimizing compilers aren’t the only reasons for performance, and languages without one can be fast

The fact that some interactive languages lack optimizing compilers often misleads the users into believing that their code is suboptimal for that reason, and they are surprised when otherwise correct Julia ports don’t improve performance.

  • “Slow” interactive languages can load binaries compiled from fast (and not-so-interactive) languages that occupy almost all of the runtime, a negligible difference from using the fast languages directly. Practical languages, including Julia, generally do this to avoid the maintenance burden of reinventing wheels. The limitation is that loaded compiled code can’t be further optimized together or with the wrapper language, but that isn’t a guaranteed loss of performance, especially if the bottleneck is elsewhere.
  • A compiler is not designed to change the code’s meaning, so performance primarily depends on the algorithm. If you’re observing great performance and optimizations in seemingly simple code, a very good developer implemented it for you.
  • Compilers don’t do all known optimizations. That’s why there are libraries with platform-dependent routines, even in assembly.

Misconception: Multiple dispatch generalizes single dispatch in object-oriented programming, so the runtime overhead must slow down the program
Reality: Multiple dispatch usually occurs at compile-time and is not a generalization of OOP

I’m guessing that this is a mixture of overgeneralizations in the few relevant Wikipedia articles and some misinterpretation. Compilers for many languages can dispatch calls at compile-time over inferred dynamic types, and Julia’s Performance Tips are largely about achieving multiple dispatch at compile-time to enable compiler optimizations. It’s similar in principle to function overloading, which is multiple methods over static types. While multiple dispatch does generalize single dispatch and single dispatch is often represented by object-oriented languages, multiple dispatch in Julia (and CLOS) is not compatible with class encapsulation of methods, the basis of OOP and its particular perks.

Misconception: Multiple dispatch lets us use any libraries together
Reality: Composability is more flexible but not effortless to implement

Multiple dispatch and JIT compilation does facilitate independent packages working together or extending each other, e.g. Package1.function1( a::AbstractArray) working on an instance of Package2.Type2 <: AbstractArray, which falls under the systems principle of composability. However, that does not mean that any two packages are automatically composable. Package-mixing calls can fail upon missing methods or incompatible types, and although Julia can catch these during precompilation given enough static dispatch, Julia does not yet have a formal interface system to facilitate method implementations in isolation. These are the relatively convenient fraction of API issues. Algorithm bugs typically must be caught by runtime tests, and subtler wrong assumptions are sometimes only challenged by an independent package down the line. Composability and its principles also exist in other languages, often referred to by different terms like duck typing, and like any good feature, it needs effort, tests, reports, and patches.

Misconception: Compiler-inferred types are a form of static typing
Reality: Julia is considered to be strictly dynamically typed

Type inference and static dispatch during compilation has led a few people to assert that Julia has static or gradual typing, but that miscategorization is based on a divide between static and dynamic typing based on a handful of language implementations. The divide breaks down beyond that e.g. a GHC option to defer compiler-detected type errors in Haskell to runtime. The typical key points that justify Julia’s strict dynamic typing are:

  • Method dispatch is entirely based on the types of input objects. There is no separate dispatch feature over the compiler-inferred types associated with expressions.
  • Compiler-inferred types are not on the language level, and the perk is allowing type inference to improve across patches and minor versions. Optimizations do typically occur if they match the runtime types exactly, but optimizations can also occur if they don’t.
  • Although a type can be declared on the language level for a variable or field, it is still a runtime type that restricts the assigned object’s, and it is allowed to be compiler-uninferrable e.g. var2::typeof( (var1::Ref{Any})[] ) = value can only infer var1[] as Any, but its runtime type restricts var2 instead. To be fair, these declarations are usually used for the same benefits as other languages’ static types are; for example, structs in practice rarely have the default ::Any fields.

Some languages do refer to features less important than Julia’s declared types as static typing, but those are more easily justified as expression-associated types on the language level, and it’s worth acknowledging that there isn’t a universal and precise concept of dynamic vs static typing, let alone other programming terminology.

26 Likes

Rang a bell, which is something Brian Kernighan wrote—rather than resorting to wishful thinking to improve performance by piling on “optimizations,” find a better algo.

1 Like

If the title promises “frequent myths”, then each section heading should state such a myth (e.g. “Julia aspires to be the last programming language”) and not its negation (“Julia does not aspire to be the last programming language”). Otherwise the section headings become quite confusing.

20 Likes

I’m not sure if the promise of an easy transition from Matlab, Octave, Scilab, etc. to Julia is also a myth, but if it is, this other post better describes the challenges faced by Julia beginners than anything else.

1 Like

@Benny It doesn’t look like writing targeted at beginners at all.

3 Likes

I think you should reorder the myths on dynamic typing and multiple dispatch.

The actual myth that you’re battling is that many people don’t have a very clear definition of static vs dynamic types in mind.

I would emphasize that static types are a property of a program, while dynamic types are a property of a program execution, i.e. julia types are mostly a property of values, not of expressions – and there is a halting-problem sized gulf between the notions.

I like how you brought up “The perk is allowing type inference to improve across patches and minor versions.” – this is really key, static types of expressions mostly don’t exist in julia, and hence do not influence program semantics, and therefore allow opportunistic tricks; as opposed to e.g. C++ auto needing a human-understandable static type on which all compilers agree, because of overloading (multi dispatch that depends on static types).

Inferred static types of expressions / variables in java / C++ are not the lowest upper bound that the compiler can prove, they are prescribed by spec (lots of room for compilers to prove better upper bounds and devirtualize!).

You can and should bring up that fields often should have static types – data structure / struct definitions in julia tend to be statically typed to some degree.

You might want to bring up some reference to the grand “dynamic type based function specialization” thesis that underlies julia’s design:

If the runtime/compiler specializes all functions on the concrete runtime types of all arguments (and does not erase type parameters), then call-sites tend to be monomorphic and types of expressions tend to be stable, while still allowing wide re-usability of compiled code.

Jeff might have found some good words for that in his phd thesis, but I don’t remember these words. And this is not trivial: If you specialize f on all arguments then sure, call-sites within f tend to become more stable, but all call-sites of f will become less stable. It is not obvious that this can all work out well in the end, i.e. that the number of runtime dispatches / type-checks will decrease in the end!

Compare to function specialization issues for other JITs / profile-guided optimization, e.g. java. (but profile-guided optimization would be awesome for julia! And I think there could be room for a mostly statically typed language based on the same grand thesis of type-based specialization)

3 Likes

I didn’t expect any feedback after a couple days so this was a pleasant surprise. A lot of what was commented were on my mind, but I ended up leaving them alone because I didn’t think of any good resolutions, so thanks in advance for any help.

This is actually what I had before, but I’ve noticed that people sometimes come away from these listicles believing the myths instead because they’re just skimming. I normally would think that people should read things carefully, but as this started approaching 1000 words, I prioritized making the headings as plain as possible. The goal after all is to reset expectations going into Julia, not to describe Julia itself (which is why I minimized Julia syntax as much as possible), so if readers only skim the headings or read a particular section before they do the much more important work of learning a language, I felt I should let them do that. Perhaps I should clarify that as well.

Still, I couldn’t figure out a nice word for a negation of a myth. I thought of “debunk” but I didn’t want to imply that anybody intentionally manufactured these myths, and I didn’t really want to get into how loose language might’ve caused them. I had “clearing up misunderstandings” before I gave up. So if anybody has a good word or expression, I’ll do a bigger edit and work that in.

I loosely addressed this by saying other languages have different opinions on how to program, but I didn’t want to get into direct language comparisons, let alone branch out into more topics like workflows and tooling. I think I should write something about it, maybe that language choice usually is more about other factors, at least dispel the notion that choosing Julia is an easy switch of a tool. Maybe I’ll figure it out in the broader edits. By all means, if someone wants to link resources on more specific language transitions, do it if you think it’ll help any readers down the line.

I’m not exactly sure what you mean here, but I did have concerns that I was talking about too much that many people would reasonably not know. I did try to simplify it as much as possible, but there’s a point where simplifying it further would result in loose, handwavy phrases that caused many of these myths in the first place e.g. “walks like Python, runs like C” is often misinterpreted as Julia being more similar to either language than it actually is. Totally open to tips, but I’ve decided pretty firmly on precise language and easily identifiable browser search terms for people who want to learn more.

I did consider it, but as part of making this skimmer-friendly, I decided to order it by how often these misunderstandings come up. People seemed to care more about what multiple dispatch does (and doesn’t do), so I put that higher on the list.

I really liked that last phrase, but I ultimately decided against it because I didn’t want to imply type declarations of variables and fields didn’t exist or were unimportant and didn’t want to argue about whether static types are a property of the written program or its implementation (people often don’t distinguish the type system from the implemented type checking). I ended up saying “types of runtime values” in the first bulletpoint instead.

Part of the problem was that it didn’t seem like anybody had a clear universal definition of static and dynamic types e.g. if Python’s runtime-ignored type hints can count as optional static typing, then Julia’s much more important type declarations given prior const names would count more. That’s why I explicitly noted the contextual opinion and lack of universal agreement, and why I went to the trouble of demonstrating that Julia’s type declarations are runtime values that can be uninferrable in some situations (as opposed to auto, decltype in C++ as you mention).

1 Like

FWIW, I think “Clarifying common misconceptions” or “Clearing up frequent misunderstandings” (or some mix of the two) would be perfectly fine.

I also agree that repeating the falsehoods as (sub)headings has risks and its nicer pedagogically to put the correct things as headings (i.e. with emphasis). Alternatively, one could try to state both the misconception and the correction, e.g.

Misconception: Composability is effortless to implement

Reality: Composability is easier to use

But that of course makes it more verbose and foes not format nicely with markdown headings. So I am not suggesting this a serious option and just wanted to put it out there as an idea.

3 Likes

Hmm, I understand how you must feel. To be honest with you, from my perspective, it sounds more like an issue with the VC portfolio manager - maybe just a lack of basic understanding.

The headings are not myths. They are the counterstatements.

I think I’ll just do that. It’s just a handful of extra sentences and it’s the part of a listicle that people reasonably pay attention to.

1 Like

That would be greatly appreciated. I guess, I consider myself among those who value your comprehensive and insightful contributions. It is worth noting that there exist significant investments associated with the Julia programming language; therefore, it is understandable that the dissemination of misconceptions should be approached with caution, this remark is made, of course, with a touch of humor. Moreover, any well-founded analysis or strategic insight concerning Julia’s potential to expand its market presence and enhance its competitive position relative to other programming languages would be of significant relevance from both an academic and an applied perspective.

1 Like

I’ve always been puzzled by the phenomenon of language users wanting to see their favorite at the top of the latest league table of popular languages. Popular languages tend to ossify as they become captured by inertia of a large user base resistant to innovation. And they become more bureaucratic.

3 Likes

But the pros of working with a popular language is that there are more libraries and examples to draw from, but there is probably a sweet spot where it’s adopted enough by capable enthusiasts but before it’s overrun by universal corporate adoption.

2 Likes

@technocrat I share the opinion expressed by @hatmatrix. I think this is especially true from an applied perspective. I just wanted to add, I didn’t say it at the top. I wouldn’t mind it happening, by any means.

1 Like

TL;DR:

Misconception: Julia is magic.
Reality: It ain’t. It just enables you to do magic.

3 Likes