Julia 1.0
julialang.org
julialang.org
Nim's home page¹ shows a piece of sample code right at the top. Perl6's page² has a few tabs quickly showing some patterns it's good at. Golang³ has a dynamic interpreter prepopulated with a Hello World.
Julia's home page shows a nice feature list and links to docs to deep dive but it doesn't do a good job of selling it.
Issue filed: https://github.com/JuliaLang/www.julialang.org/issues/115.
Lisp languages do take a long time to get used to.
The first impression does leave me thinking that using Julia for different programming domains like distributed internet-facing servers or web services is not something it was built for.
No. Julia is a general purpose language that has so far been mainly focused on scientific and mathematical programming.
It's design is probably least friendly to the real-time programming domain (GC based) but it can apparently be used there as well:
I see an extremely bright future ahead for Julia!
https://github.com/JuliaRobotics/RigidBodySim.jl/blob/34ac43...
Notice that this function is purposefully sleeping the differential equation solver in order to slow it down to the exact amount to get the simulation back to real-time.
You can see how much extra time is left for that frame at the targeted frame rate, then sleep for that amount of time.
Then you never have to slow down anything else, you can set a maximum amount of cycles per second and you can sleep once per cycle. The faster the CPU, the less power it should use.
And of course, I should really assist to solve this problem myself. But, I thought to encourage other language experts.
So like Python, you have SciPy Pandas etc. but don't have to leave Python when you need to do a bunch of text processing or whatever.
At the same time, calling into other languages (eg C) is super straightforward (disclaimer: last time I checked).
http://docs.juliadiffeq.org/latest/features/io.html#Tabular-...
f_2dlinear = (du,u,p,t) -> du.=1.01u
prob = ODEProblem(f_2dlinear,rand(2,2),(0.0,1.0))
sol1 =solve(prob,Euler();dt=1//2^(4))
using DataFrames
df = DataFrame(sol1)
# Result 17×5 DataFrames.DataFrame │ Row │ timestamp │ value 1 │ value 2 │ value 3 │ value 4 │ ├─────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 1 │ 0.0 │ 0.110435 │ 0.569561 │ 0.918336 │ 0.508044 │ │ 2 │ 0.0625 │ 0.117406 │ 0.605515 │ 0.976306 │ 0.540114 │ ...
Exception handling in julia is poor, which reminds me of how exceptions are (not/poorly) handled in R. Code can trap exceptions, but not directly by type as you _would_ expect. Instead, the user is left to check the type of the exception in the catch block. Aside for creating verbose blocks of boilerplate at every catch, it's very error prone.
Very few packages do it right, and like in R, exceptions either blow up in your face or they simply fail silently as the exception is handled incorrectly upstream by being too broad.
Errors, warnings and notices are also often written as if the only use-case scenario is the user watching the output interactively. Like with R, it's possible but quite cumbersome to consistently fetch the output of a julia program and be certain that "stdout" contains only what >you< printed. As I use julia also a general-purpose language to replace python, I feel that julia a bit too biased toward interactive usage at times.
That being said, I do love multiple dispatch, and julia overall has one of the most pragmatic implementations I've come across over time, which also makes me forget that I don't really like 1-based array indexes.
I initially defended the choice, but I now agree that 1-based indexing now seems like a poor choice since Julia has become something more than the original mission of a better MATLAB or Octave. It’s a, admittedly, minor tragedy of Julia’s success.
I’m curious as to why this is a problem outside numerical computing. From my perspective, this is consistent with a long history of mathematics dealing with matrices that predates electronic computers.
0-based arrays are popular because C decided to deviate from what had previously been standard in math and in Fortran.
Is there a reason other than aesthetic preference and habit that makes 0-based indexes better for computing in non-numerical contexts?
I realize both indexing standards are arbitrary and boil down to “that’s the way grandpa did it,” however 1-based indexing grandpa is way older and more entrenched outside computing circles.
EDIT: I suppose with Julia it’s not that important, as other commenters have pointed out that you can choose arbitrary indexes.
I also thought I'd seen a longer text focusing more on the counting/indexing.
I still don't see the appeal of "element at zero offset" vs simply "first element".
I do agree that < vs <= etc can get messy. But outside of now fairly archaic programming languages I don't see the need. Just use a construct that handles ranges, like "for each element in collection/range/etc". (Or for math, "pattern matching" (or "for n in 1..m").
An unexpected takeaway: Michael Chastain's time-traveling debugging framework, which he had in 1995, and which still reads like sci-fi from the future[1].
Alas, this quote will probably stay relevant for a while:
>[We keep] finding programming constructs, ideas and approaches we call part of “modern” programming if we attempt them at all, sitting abandoned in 45-year-old demo code for dead languages.
Then the second parts quotes the creator of BCPL, revealing – a-HA! – what we already knew, that the array is a pointer to the first element and the index is the offset from the pointer, and that's why it's zero.
(And after that it veers of into complaining about how the papers documenting this history cost too much money, so they didn't actually read them.)
People argue that zero-based is incidental, and that 1-based is the right way because of it's long history in mathematics notation. I would argue that 1-based is incidental, and that zero-based is better most of the time for modern math and computer architectures.
Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".
Depends what the library function does of course. If it's shape preserving (e.g. if it's a map over the array), it'll generally preserve the axes of the input array.
> What if I do an outer product of two arrays with different base indices?
You'll get an offset array indexed by the product of the axes of the input:
julia> A = OffsetArray(1:3, 0:2)
OffsetArray(::UnitRange{Int64}, 0:2) with eltype Int64 with indices 0:2:
1
2
3
julia> B = 4:6
4:6
julia> A*B'
OffsetArray(::Array{Int64,2}, 0:2, 1:3) with eltype Int64 with indices 0:2×1:3:
4 5 6
8 10 12
12 15 18
> Whatever your answer, I suspect it is more cognitive overhead to remember than "always 1 based" or "always 0 based".Sure, but the real point here is that in most situations, you don't actually care what the axes are, because you use the higher level abstractions (e.g. iteration over elements or the index space, linear algebra operations, broadcasts, etc.), which all know how to deal with arbitrary axes. The only time you should ever really have to think about what your axes are is if there's some practical relevance to your problem (OffsetArrays are used heavily in the images stack).
This points to another example where 0-based should be preferred. When doing modulo arithmetic, 0..N-1 mod N gives 0..N-1, but 1..N mod N puts the zero at the end. I also cringe at languages where -1 mod N does not equal N-1.
For any language with a mod operator, a mod b should always be equal to (a + k×b) mod b for any integer k.
Breaking this invariant makes the mod operator useless in pretty much every application I ever have for it. In e.g. JavaScript I need to define a silly helper function like
mod = (x,y) => x%y + y*(x%y && x>0^y>0)
And then remember to never use the native % operator.The other (“remainder”) version which takes its sign from the first argument is pretty much worthless in practice. IMO it doesn’t need any name at all. But what it definitely doesn’t need is a shorter and more convenient name than the useful modulo operator. Its ubiquity is a serious screwup in programming language design, albeit largely accidental.
In my opinion neither are used sufficiently often to justify taking up a valuable ASCII symbol (same with xor).
I don’t know what is available as chip instructions, I could certainly believe hardware designers made the wrong choice.
[0] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_floo...
[1] http://www.lispworks.com/documentation/lw50/CLHS/Body/f_mod_...
As one simple example, it is frequently useful to map floats into the range [0, 2π), but I have never once wanted to map positive floats into the range [0, 2π) while negative floats get mapped into (–2π, 0] by the same code.
- https://github.com/JuliaLang/julia/issues/14826
mod(-eps(), 4.0)
- https://github.com/JuliaLang/julia/issues/17415 fld(1.0,0.2)
- https://github.com/JuliaLang/julia/issues/3127 mod(1.0,0.1)
- https://github.com/JuliaLang/julia/issues/3104 mod(0.95,0.01)
I'm pretty sure there are more issues.re: mod 2pi, typically it is most accurate to reduce to (-pi,pi) (i.e. division rounding to nearest). Also to get it accurate you need fancy range reduction algorithms, hence julia has rem2pi https://docs.julialang.org/en/stable/base/math/#Base.Math.re...
These examples don’t really have much bearing on the general usefulness of “remainder” vs. “modulo” though.
That's precisely what it does!
> I also cringe at languages where -1 mod N does not equal N-1.
julia> mod(-1,10)
9
But admittedly when you use 1-indexed arrays, any kind of modulo operator becomes pretty inconvenient a lot of the time (lots of futzing to avoid off-by-1 or boundary errors). So maybe it doesn’t matter in the Julia context.
1-based indexing has the advantage of always knowing the length of the array since the index of the last element is this value. You also get "proper" counting numbers as you iterate.
I've used both in various languages. Perl lets you define the array index start to be whatever number you wish.
Especially since it would have been very easy to 'do it like Ada' and allow any start index by default (I have use Lua and it's really annoying to use an extension language with 1-index when the base language is 0-indexed)
Until then Fortran, Lisp, Algol, Pascal, Basic never had any big issue with indexes.
Programming languages should not be configurable in that kind of way.
For a pre-baked solution: https://github.com/JuliaArrays/OffsetArrays.jl
Instead of complaining in the abstract, check it out, you'll be impressed. https://julialang.org/blog/2017/04/offset-arrays
Default matters!
(not referring specifically to you here!)
>In order to read a csv in that doesn't have a header and for only certain columns you need to pass params header=None and usecols=[3,6] for the 4th and 7th columns:
https://stackoverflow.com/questions/29287224/pandas-read-in-...
Just reading that hurts me.
Again, 0-based indexing exists to fit a purpose: http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EW...
In my opinion, reading `a = b[1:n-1]` hurts much more than reading `a = b[:n]`.
No, that's their ordinal position, and how 8 billion non-programmers would refer them as in any everyday setting.
That's also how programmers would refer to them if it wasn't for a historical accident.
What's more, that's also how programmers refer to them when they talk between then and not to the machine ("check the 3rd column" not "check the column at the index of 2").
I have taught Python quite a bit, and I have gotten good at explaining 0 based indexing and slicing based on it. When I switched to Julia there was nothing to explain. And my code has about as many +/- 1s as before...
These spoken language conventions developed before there was an established name for “zero” or even a concept that “nothing” could be a number per se.
For similar reasons, we have no zero cards in our decks, no zero faces on a dice, no zero hour on our clocks, no zero year in our calendar, no zeroth floors in our buildings, East Asian babies are born with age one year, etc.
It’s only by another set of historical accidents that we have a proper 0 in our method of writing down whole numbers. Thankfully that one was of obvious enough benefit that it became widely adopted.
In (North?) America. In Europe, there's a ground floor (zero), then first floor (1), etc. Basement is -1 (etc.).
A European friend of mine arrived at college in the USA, and was assigned a room on the first floor of the dorm. She then asked the housing office whether there was a lift, because she had quite some heavy luggage, earning some rather amused looks :-)
Whoever designed the European convention for labeling building floors was numerically competent.
Too bad medieval European mathematicians and the designers of Fortran weren’t. ;-)
I'm happy to be writing
``` for i in 1:n func!(a[i]) end ```
to iterate over an object of length n. Or split an array as a[1:m], b[m+1:n]. Slicing semantics which are far more prevalent in my code (and the code I read) than index arithmetic, are truly vastly simplified by 1 based indexing of Julia compared to the 0 based numpy conventions. We simply no longer code in the world that Dijkstra argued for, and I have not seen anybody give a clear argument that is actually rooted in maths and contemporary programming.
I genuinely thought that the Python convention was brilliant, and that 1-based indexing in Julia would suck. It turned out not to be the case.
I am legitimately (mildly) curious about the history of the different naming conventions for floors of buildings though.
> The base level doesn't need to have a floor, it's just ground. Once you add a floor you are on the first floor above ground.
Yes, my point is this is an example where the European 0-based indexing system makes more sense (in my opinion) than the American 1-based indexing system. I speculate that whoever started calling the ground floor the “first floor” hadn’t really put much thought into how well that would generalize to large buildings with many floors including some underground.
Similarly, whoever decided the calendar should start at year 1 AD with the prior year as 1 BC hadn’t really considered that it might be nice to do arithmetic with intervals of years across the boundary.
There are many standard mathematical formulas which are clarified by indexing from 0. But nobody can switch because the 1-indexed version is culturally fixed. Most of the rest of the time the 0-indexed vs. 1-indexed versions makes basically no difference. It is rare that the 1-indexed version is notably nicer.
> Or split an array as a[1:m], a[m+1:n]
Yes, I find it substantially clearer to write this split as a[:m], a[m:]. Particularly when dealing with e.g. parsing serialized structured data. But also when writing numerical code. Carrying the extra 1s around just adds clutter, and forces me to add and subtract 1s all over the place; reasoning about it adds mental overhead, and extra bugs sneak in. (At least when writing Matlab code; I haven’t spent much time with Julia.)
There is. We call it 12 for some crazy reason (it goes 12 AM, 1 AM, 2 AM, ..., 11 AM, 12 PM, 1 PM, ...).
> no zero year in our calendar
Which is quite irritating really. New Year's Day 2000 wasn't the start of the 3rd millenium, because there was no year zero.
> East Asian babies are born with age one year
But not western babies.
I don’t know the history of reported ages of Western babies.
> quite irritating really
Yes that is my point.
On the other hand, I'm wondering if this will help a little with the dependency hell that's caused me to drift away from Julia over the last year or so.
At first I was fairly excited about Julia, and greatly preferred it over R or Python for numerical work, library resources aside. It was fast and I liked the language design itself.
Over the last year or two, though, I've had recurring problems with trying to install and use packages, and them failing at some point due to some dependency not working. Sometimes the target package itself won't install, but most of the time it's some lower-level package.
It's more frustrating than not having packages available, because it creates some sense that packages are available, when they're not. At first I looked at it as some idiosyncratic case with one package, but it's happened over and over and over again.
Basically in this time I've given up on Julia, because there's such a huge discrepancy between what it looks like on the surface and what it looks like in practice, once you get out of certain limited-use cases, or when you're not coding everything yourself from the base language. (Related concerns about misleading speed claims have been raised, although my personal experience in that regard has been mixed, because my experience overall has been pretty good in some critical performance cases, but there have also been some wildly unpredictable exceptions that act as bottlenecks... but it's still much better than R or Python).
When I've tried to figure out what's going on with dependency hell, usually it's because some package that was previously working with a earlier version is no longer working. So maybe stabilizing the language will help that?
With the new binary builder, binaries should be another note, so long as you download an official Julia binary, or built Julia from source in the same way those binaries are made (eg, build OpenBLAS with gfortran 7).
If you can, try and find a few hours to help pitch in and solve the package problems, even if it's just updating docs or updating deprecated-but-useful packages.
The current major issue, as it stands, is that it's very easily for a malicious bit of code to sneak into a heavily used JS package and have oversized effects - this happened very recently with a very popular linting-support package.
The other issue is general posting of malicious packages under mistyped names, or takeover of existing packages with malicious updates by new owners.
At the same time, nobody wants to have NPM (the org) manually vet every upload ever made. So, there's that.
Many JS packages are extremely dep heavy, overwhelmingly for minor features (checking something is an array, promise-fying FS, etc) which makes it very easy to infiltrate packages and very hard to vet a package entirely.
Finally, npm (the program) runs into a fair bit of caching woes and it's own dumb bugs which feel like they shouldn't slip into production nowadays. Oh, and sometimes npm (the website) goes down.
The answer for JS, unfortunately, is probably segmentation - as better managed and more secure package repos come up, likely with their own package managers, npm will probably have to up their game. That, I am sure, will bring a whole fresh set of issues.
But more generally things are maturing.
And 1.0 will help too, since things won't be chasing a moving target.
If your not in any hurry, I'ld give it 6 months, of people who don't mind a bit of packages breaking (e.g. people like me) using it.
That will be plenty of time for everything to shake out. More than you might expect has actually already been shaken out in the last few weeks in the package ecosystem. Hitting 1.0 should give some package maintainers the drive to get it done.
Exception handling is indeed somewhat of a wart with the core system not having changed much since very early on. I think there's still some serious design work to do and the core people take it seriously. On the other hand, I suspect that numerical codes don't want to use exceptions for much other than "panic and escape to an extreme outer scope". So a lot of the scientific users are probably content for the time being.
That said, I am optimistic Julia will have a good solution at some point: contextual dispatch, a la Cassette.jl, enables just the sought of interventions you want for error handling. I'm not quite sure what the result will look like, but I imagine you will see some experimentation in this direction in the near future.
You can get around some of the pain of haning error types in catch statements if you are comfortable paying the price for a locally defined function:
result = try Something() catch err h!(e)=throw(e) h!(e::MethodError) = SomethingElse() h!(err) end
This pattern works well if an error case should return a default value and all others should throw.
For example, the fact that an array of unions such as Array{Union{Missing,T}} is represented in memory as a much more efficient union of arrays is a perfect example of where a clever compiler can make the logical thing to do also the efficient thing to do!
Once you make that distinction, then whether you write it as a Cython module exposed in Python or you can use native language features to do it in Julia, nobody cares. It’s encapsulated away from people who use numerical libraries, as it should be.
I also spend time developing these “runtime overhead avoidant” backend numerical libraries, and I would say I’ve seen no significant reason to prefer Julia over Cython.
Don’t getme wrong, Julia is great, just not offering anything fundamentally different. And since there’s already a critical mass of people with engineering and optimization experience in the Cython & Python extension module stack, I’d expect that community to continue dominating Julia just by attrition alone.
http://www.stochasticlifestyle.com/why-numba-and-cython-are-...
Modern numba can also do a lot more for huge scale projects than what the article suggests.
Julia docs also seem very smugly proud of multiple dispatch and autogenerating implementations for multiple types or signatures.
But Cython fused types allow the exact same polymorphic multiple dispatch. Here’s an example pedagogical project illustrating that point.
< https://github.com/spearsem/buffersort >
This pattern is quite easy and offers a lot of generic strategies in Cython, especially if you just want a bunch of overload options in a pure C backend with a thin entrypoint to Python.
Cython fused types are required to be known at the package's compile time, and that's exactly the point that makes it less composible. Not requiring this is exactly the advantage of Julia which is demonstrated.
The article doesn't say Numba cannot do huge scale projects. It just talks about the difference in the compilation strategies. If you read that and think that it means Numba isn't suited for huge scale projects, then that's your interpretation of how Numba works, not an opinion directly stated by the article.
The article gives an easy way for you to demonstrate it wrong. You can show 10 lines of code where you send dual numbers and uncertainty objects through SciPy's ODE solvers to generate solutions with gradients and error bars. If Cython can do this, please show it.
Besides, please show me that 10 lines of Cython that shows it can AD and throw numbers with uncertainties through SciPy's ODE solvers since it's just as flexible as Julia. It should take less characters to write than your previous response!
> “It's not hard to get a loop written in terms of simple basics (well okay, it is quite hard but I mean "not hard" as in "give enough programmers a bunch of time and they'll get it right"). Cool, we're all the same. And microbenchmark comparisons between Cython/Numba/Julia will point out 5% gains here and 2% losses here and try to extrapolate to how that means entire package ecosystems will collapse into chaos, when in reality most of this is likely due to different compiler versions and go away as the compilers themselves update. So let's not waste time on these single functions. Let's talk about large code bases. Scaling code to real applications is what matters. Here's the issue: LLVM cannot optimize a Python interpreter which sits in the middle between two of your optimized function calls, and this can HURT.”
You seem to be disingenuously recasting what’s written in the link to serve shifting purposes, to the point that I am skeptical you’ve even got a coherent claim here in your comments.
2. The quoted text talks exactly about separate compilation, like the comments above do. So it's really unclear where you take offense. Maybe read it again carefully?
3. I have championed numba for a long time, and am now happy to jump to Julia, for pretty much those reasons. I have implemented time critical parts of the code in numba, and that has forced me to reimplement a whole bunch of standard algorithms rather than relying on library implementations. Need sparse matrices in your hot loop? well you can't just call scipy sparse. That's the fundamental disaster of numba. You sit in the middle of the most amazing ecosystem and you can't use it.
Authors can have inconsistent opinions, and can sometimes try to retroactively reinterpret something they wrote to serve a different purpose later on. That is happening here. It’s not a big deal; I’m just saying for this reason I don’t agree with your claims about my comments, nor your overall comments about numba (even after re-reading your link and comments and deliberately reflecting on them).
With some persistence Python-Cython-Numba can get the work done, but to me it has never felt like a consistent whole, usually a tagged on hodge podge. Numba is cherry picky on what it will and will not optimize, I totally understand why it is so. It has always been a 'will it or wont it" with Numba.
I think the potential that Julia offers that Cython and Numba doesnt is the option of a programmable syntax for the low level manipulation. Cython has improved, but for the longest time it was always a forced decision between operating at a the higher level of array/sub-array manipulation in Python or down to tedious index manipulation in Cython with nothing in between. Not to mention fighting the Cython compiler and Python's C API to get the 'yellows' out.
I don’t think Julia’s generic code is the end game for fast code though. Efforts in Python toward code gen include projects like loopy, which targets accelerators, are still developing but make it easy to decouple kernels from loop domains, target specific devices and optimizations etc.
In fact I think it's a much better thing to have a sort of division where a general scientist can put together a simulation/analysis pipeline in Python or R, but if they want to implement new algorithms they'll be sufficiently out of their league that they'll need help from someone who has actually spent time learning how to code properly and efficiently, to do testing and version control etc.
In fact it's very similar to how experimental science often works: you have the general scientist who just knows how to do basic measurements with some instrument, and you have the instrument scientist who has to help if the general scientist wants to use some novel technique, who keeps the equipment tidy and working and logs everything in the big lab journal.
... and it's been wildly successful. Some of the most prolific and talented contributors to Julia and its ecosystem are scientists by day and brilliant programmers of all kinds—crazy meta programmers, compiler hackers, generational GC writers, etc.—by night (as they say). During the keynote last night before tagging 1.0, we went through the history of major features in Julia's development history, it became a running theme that so many of these contributions have come from people who are physicists, chemists, geologists, biologists, etc. So I'd say that Julia is strong evidence that breaking down the separation between scientific users and developers of scientific libraries is a good idea.
As a former physicist, I have been through the Numerical Recipes/F77 stage myself two decades ago. In the age of Big Data and Machine Learning, there are many ways to harvest the creativities in people who are not trained as professional engineers. Our software development mindset should be extended beyond the industrial "task oriented" products and platforms.
In the spirit of Turing machine and LISP where code is also data, we can even treat scientific computing ecosystems like Python/R/Julia etc. as models to capture creative scientific minds. It is AI at a high social level. The future is beyond our imagination.
We need more crazy meta programmers.
Thank you, that's very kind of you! To be fair, Jeff, Viral, Alan and I had many collective decades of industry experience interleaved with an equal amount of academia by the time we wrote that paper. And of course the academics are rather suspicious about just how academic we really are.
> We need more crazy meta programmers.
Hear, hear!
And when I want Julia’s promise of fast loops, I use Numba. If all the effort gone into Julia had instead been spent on fixing remaining warts in Python workflow for science, we wouldn’t even havee this conversation.
An example of the value of generic code is that forward mode AD is extremely easy, and almost always just works on whatever code you run it on.
Then, once that's done, multiple dispatch (and possible macros for a DSL) allows for a much cleaner user interface than Python offers for numeric code.
I have a lot more experience with R than Python, but seeing more of the scientist/mathematician/researchers side of things, I have to strongly disagree with the view that they should write slow code and contact a CS guy to write fast code in another language when they need it. Do you honestly think that's practical for grad student's projects? Recently, one of my friends wrote a simulation in R. Most of the work was done by hcubature -- a package written in C -- integrating a function written in R. Could just have easily been written in Python. That function was slow, and the simulation ran for days. Before an error caused it to crash, losing days of compute time. I -- a statistics grad student -- helped him rewrite it in Julia, and it finished in 2 hours.
That C/C++ code will still run slowly if they have to call your R/Python code is a problem. They also can't apply things like AD easily. A common solution, used by Stan for example, is to create a whole new modeling language and have users interface through that. Learning a new language -- albeit relatively simple/domain specific -- which they then cannot debug interactively, is another pain point. All this can be avoided by simply using Julia.
Although AFAIK it hasn't really in the case of R, outside of tidyverse.
As a grad student without a CS background, I don't think I'm qualified to say much more on this.
Hadley Wickham is special because he has both the stats, data science AND programming skills.
data.table is also an amazing library. R to me is the most improved language in the history of programming languages over the past 5 years.
Also R allows anyone with basic hackery R skills to create libraries easily and that is why so many of them are not optimal.
Where do you think most companies get "professional programmers" from, exactly?
Julia's been designed and implemented by some very bright people, and it shows.
Grad students may be brilliant but that does not help give them any insight in to what makes a good ecosystem, toolchain, and feature set good.
More seriously: part of the problem does seem to be that Julia does have some significant differences from "traditional" languages (e.g. the concept of a "virtual method" is a bit fuzzy in Julia, what we call a JIT is probably better described as a JAOT, whether it has a "type system", homoiconicity, etc.).
That said, this JuliaCon I have met a lot more people from and classical "programmer" backgrounds. So hopefully that is changing.
What I see on Github is as professional as it can get. Issues, discussions, triage, review, CI-tests for example.
Maybe you started too early, before Julia was settled? And/or were too over-enthusiastic to begin with? I think Julia had to grow, find the 'correct' solution with e.g. NA/Missing/Nullable. Break things b/c it didn't work out as expected. Postpone things, debugger (maybe?), for more important areas or because base was not stable yet.
Two years ago in a project I hoped that people would switch immediately from R to Julia. But in retrospect it was good they didn't. Julia was not ready for them and too much ecosystem stuff missing/unclear still. (This said, Julia would in principle have been much much better suited for that project).
Refactorings and major changes in ZMQ.jl and the web stack similarly get merged and released immediately with zero review, still. This is a major problem.
Features in the base language have been deleted during 0.7-DEV because a single core developer didn't like them, despite multiple other core developers voicing disagreement that the features were useful and removing them was not urgent or necessary.
It's not a development culture I would rely on when products and money and jobs are at stake. Even the startup you were working with abandoned julia, correct?
What I don't understand is why you didn't just stay with old stable versions? You wouldn't be exposed to such issues, wouldn't you?
> It's not a development culture I would rely on when products and money and jobs are at stake
On the other hand this 'development culture' has brought brilliant results in a relatively short amount of time with a relatively small team.
There was a talk [1] at the Juliacon 2018 where a company very successfully replaced an IBM product with Julia code. At 48:07 there was a question 'about problems with changes in Julia'. Answer: they started with v0.3 and 'didn't really have many problems'. They 'didn't use anything particularly exotic'. So, yes, I'd say if you adapt to the given situation it can (could have) work(ed).
I'm not convinced that a non-cowboy style would have been better. (And besides, this doesn't come free moneywise).
Talk to me when google, amazon, microsoft, facebook etc are publicly using and officially supporting julia on cloud platforms or even infrastructure libraries like protobuf.
The carelessness isn't responsible for or helping anything. A good diffeq and optimization suite have been built despite the prevalence of careless practices, not because of them.
It's not a question of money either, just patience and code review and recognition of how many things downstream are going to be affected by mistakes. You'll save more time in not having to put out as many fires than it will cost to slow down and not be in such a rush at all times.
https://github.com/robclewley/pydstool/blob/master/README.rs...
If you think these are poorly maintained you should see XPPAUT, a tool still quite widely used.
And it wasn't Python 3 for pydstool. It's SciPy 1.0.0. Some of the recent maintenance for this stuff has actually come from the Julia devs though:
Yeah sorry, I was just acknowledging that I was wrong when I found the PR and noticed the mistake. I guess it come across oddly.
If you have anything that requires more complications, numba becomes painful. You seem to somehow insist that your usecase is the only one out there. We are actively developing a scientific simulation library in Julia. The prototype was in Python+numba. The Julia code is vastly simpler, and that is because Julia is not "an interface to LLVM for fast loops". It's a full fledged language with performant abstractions, closures, inline functions, metaprogramming, etc. To get things fast in numba I ended up doing code generation (I talked to the Numba developers, it seemed the only way). Talk about brittle, painful and impossible to generalize.
Now we have Julia code, using sparse matrices in the hot loop is easy, Automatic Differentiation just works, etc...
The correct comparison for Julia is this context is C++, not Python.
I further want the user of the library be able to pass it new functions that can be integrated into overall dynamical behaviour.
There are different ways to achieve this, the simplest version is with closures. Pass a list of functions, and some parameters and I construct a right hand side function from it. Unfortunately this does not work with numba. What I ended up doing is passing not the function itself but the function text to generate the code of the function to be jited and then eval that. It worked but it was horrible to maintain, and required users to pass function bodies as text witha very specific format.
Now in Julia we will probably eventually transition to a macro based approach, but the simple closure based model just worked.
Previously I had large scale, inhomogeneous right hand side functions that I wanted to jit in numba and that need sparse matrices. So I ended up having to implement sparse matrix algorithms by hand because I can't call scipy.sparse.
Another instance: I implemented a solver for stochastic differential equations with algebraic constraints in numba, partly to be able to use it with numba jited functions and get a complete compiled solver out of it. This already constrained my users to use numba compatible code in their right hand side functions.
In order to get this to work I had to implement a non-linear solver from scratch in numba rather than being able to use scipys excellent set of solvers.
Julia is not a magic silver bullet. Getting the ODE solvers to make full use of sparsity still requires some care and attention. But I simply spend a lot less time on bullshit than before. (so I have more time to spend on HackerNews :P)
I decided to switch over when for one paper I was able to implement a problem using the standard tools and packages available in Julia within half a day. The Python equivalent would have involved using a new library that came with its own DSL, which would have meant rewriting quite a bit of my code to take advantage of it. Easily several days work.
With DifferentialEquations.jl I also could just test half a dozen different numerical algorithms on a problem in a matter of minutes, find out which performed best and use that for MonteCarlo. Saved about a week of computation time on one project alone. That's not a critical amount, nobody cares if the paper comes out a week later or earlier, but it's nice (and I don't waste super computer time). With Python libraries with different DSLs this would have taken considerably longer, and I probably would not have done it. This is the result of having one library and interface rather than a whole bunch, if everyone agreed on scipys ode interface (which just got properly established in scipy 1.0.0) this would be easy in Python as well. But that's also the point that people have been making: Julias design for composition over inheritance makes it convenient to rally around one base package.
I also personally very much like being able to enforce types when I want to. This is a big win for bigger projects for us.
I love it when libraries limit what can be done with them and document an extremely specific scope they apply to.
When libraries try to be all things to all people, it’s bad. A sophisticatedcode gen tool that enables library authors to choose to do that is a bad thing, not a good thing.
I have ideas for a more general library of course, :P But I'm not spending time on them.
yep... I took a look at the DE packages in Julia today, and quite frankly they're much better than the situation in Python, perhaps because of one or more prolific applied mathematicians are making a concerted effort, which is lacking Python? I dunno, but I did recommend my colleagues look at Julia for DEs, for this reason.
That said,
> Pass a list of functions, and some parameters and I construct a right hand side function from it. Unfortunately this does not work with numba.
I'm pretty sure I've done this before with numba, so maybe getting concrete would help, e.g. an Euler step
def euler(f, dt, nopython=False):
@numba.jit(nopython=nopython)
def step(x):
return x + dt * f(x)
where user can provide regular Python function or a @numba.jit'd function. If a @numba.jit'd function is provided, and nopython=True, this should result in fused machine code. This sort of code gen through nest functions can be done repeatedly for e.g. the time stepping loop.I've done this for CPU & GPU code for a complex model space (10 neural mass models, 8 coupling functions, 2 integration schemes, N output functions, ...) which, by the above pattern, results in flexible but fast code.
Is this a pattern that captures your use case or not yet?
> implement sparse matrix algorithms by hand because I can't call scipy.sparse.
agreed, this is a surprising omission, which I attribute to not much of the numerical Python community making use of Numba, but could be fixed rapidly.
> constrained my users to use numba compatible code in their right hand side functions
what did you run into that was problematic?
> I had to implement a non-linear solver from scratch in numba rather than being able to use scipys excellent set of solvers
I didn't follow; passing @numba.jit'd functions to scipy is in the Numba guide, so what exactly didn't work?
The library we're building now though does something different. Something like this:
def network_rhs(fs, Network)
def rhs(y,t)
y2 = np.dot(Network, y)
r = empty_like(y)
for i, f in enumerate(fs):
r[i] = f(y2[i])
return r
return rhs
> what did you run into that was problematic?For more complex model building the right hand side functions actually make use of fairly complex class hierarchies. That was the major stumbling block. But people also were using dictionaries and other non-numpy data structures and just generally idiomatic Python that is not always supported. Some of that stuff is inherently slow/bad design of course, but it still ended up killing the use of my solver for this project.
They are now rewriting in C++, which is absolutely a great choice for their case (and probably would have been viable for us too if we had had more people with a C/C++ background in the team).
> passing @numba.jit'd functions to scipy is in the Numba guide
I wanted to use scipy.root from numba. Not the other way around.
Now if all of the numerical Python community was standardized on numba, a lot of this would not be an issue. Scipys LowLevelCallable is a great step in the right direction. But fundamentally I don't see how you will ever get the different libraries to play together nicely in a performant way. It would require every API to expose numba jitable functions. Last I checked, the only functions you could call from within numba code were other numba functions and the handful of numpy features the numba authors implemented themselves (I remember waiting for dot and inv support). If I have an algorithm by a student implemented on a networkx graph as a data structure I can't just jit that. In Julia it automatically is.
The churn is exhausting but I see the merit of starting over and getting everything done in a fully fledged JITd language.
I get the issue with scientists writing poor code, but Numba has largely solved this problem, by packing an LLVM JIT into a decorator which can be applied to any numerical code to get same speed ups as Julia, except no language switch required.
Citing slow code in the wild with a fast rewrite is a hilariously poor anecdote performance wise. I’ve rewritten Fortran code into Python and gotten speed ups. Regardless of the language, garbage in, garbage out.
Stan is an example where the modeling is “just” a DSL implemented as C++ templates. Does that make that a good choice?
The same things that happened when we had this conversation about what happens to the Fortran writing scientist when Python and Numpy came along, even at that time it wasnt the first time. I am sure it would not have been a whole lot different when a COBOL alternative had come along.
My point with the rewrite was not garbage in, garbage out. It was that even though the original R code was using a library written in C, that library had to call a function he wrote in R millions of times. That R function being inherently slow is part of the problem. (The easiest fix for that is just writing the function you pass to that library in RCpp, but the overhead on that is still close to a microsecond -- not sure how it is in R. Numba is probably easier.) It is nicer to not have to worry about that.
An alternative approach some libraries provide, like my Stan example in RStan or PyStan, is the DSL they implement to make it easier for end users in R or Python to write fast C++ code.
But, now lets say you're working on an optimization problem. You want to use a gradient-based optimization method, while your code is heavily dependent on the FooBars and Widgets libraries. If these libraries are written in Julia, you can write code in Julia using these libraries, and automatic differentiation will just work as you pass it to Optim.jl.
If FooBars and Widgets were Cython libraries, optionally wrapping C/C++ code, would this work? Could you write functions making use of these libraries, and get efficient gradients for optimization for use with an optimization library and have everything be fast?
'Stan is an example where the modeling is “just” a DSL implemented as C++ templates. Does that make that a good choice?'
I gave Stan as an example of a less than ideal situation, because people normally use Stan from R or Python, not from within C++. Therefore there R and Python don't integrate well. If you're already working in C++, Stan seems ideal. You can use arbitrary templated C++ code with Stan, include external libraries, etc. It needs to be C++ because of their autodiff.
And sure that wouldn’t work with arbitrary Cython modules because Cython was designed as a Pythonic syntax over the Python C-API, and it just happened to become popular for numerical work.
I don’t think that’s a strong argument, though, because anything small enough to be usable with AD can be rewritten without too much time lost, whereas those massive Fortran routines with iterative algorithms wouldn’t produce useful gradients in any language.
>I don’t think that’s a strong argument, though, because anything small enough to be usable with AD can be rewritten without too much time lost, whereas those massive Fortran routines with iterative algorithms wouldn’t produce useful gradients in any language.
No. In Julia you can just stick the entire delay differential equation solver into the AD functions and get a gradient for parameter estimation. Saying you cannot use an arbitrary Cython code is a limitation, and saying you cannot put a random large code into AD is a limitation. It wouldn't be an issue if these weren't already solved problems, but having a performant software with simple and available AD is not something that's unreasonable anymore. If you use Julia, it's just something you can expect to work.
Seriously, read this issue:
The numba story in that github issue mirrors my own experience: Excitement! This works! It's fast! Ok here are some limitations that I can work around. Hmm I would really like to use this library, in principle it should be possible to JIT its output/make it JIT compatible. In practice this turns out to be way to subtle. Ok I'm giving up, either I reimplement an algorithm directly in my hotloop or run slow code.
So yeah, as long as your problems do not cross the domain of one package, Python and its ecosystem is great. Stay with it. But I fully expect that we'll see a lot more innovation in Julia. Already now there are classes of problems for which no Python solution exists but which actually have library support in Julia. That's really really remarkable.
Despite my misgivings about the state of tutorials, the release handling process and the aproach to tooling, this is why I switched already. I also hope that all these aspects will improve post 1.0 massively.
It's genuinely a liberation to no longer be confined to silos of DSLs that do not allow for low cost abstractions.
It's fine if you don't need it. I just don't understand why you hang out in a thread about Julia insisting that I don't need it either and could just use numba + Python when that's exactly what I've been using prior to Julia.
But that's not a issue resolved by any particular language; Julia appears to be free of lock-in because it hasn't had time to develop multiple, exclusive approaches to the same problems. Perhaps Julia builds into the language the ultimate performance solutions, so ok, then, for example, wait until there are N different web frameworks, and there you will find your silos. Python has many approaches to making things fast, which is why there are silos. /shrug
> I switched already
I probably would too if I was still a grad student.
> why you hang out in a thread about Julia
I was perusing whilst waiting for my Python code to complete, when I saw someone suggesting Python is already quite OK, catching some hate. I'm more than happy for the Julia community, but I think it's helpful to get exchanges accurate and critical.
I think Julia looks very sexy and students jump on that, often without considering whether they will be their actual work done or spend time porting libs or debugging things I can’t help them with.
I was also and continue to be worried about the tooling, the lack of good tutorials and especially the Type driven system. I like it so far but Object Oriented is a lot more familiar to many people. The library situation for me specifically has tipped to be a net positive. I also could transition my very small team off Python completely.
So it was not an ad hoc decision, I tried it several times over the last year's and decided it's not there. In my specific situation, with a rewrite of a core library coming up and the library situation being there that changed early this year.
To be clear, I don't remember needing to do this as a regular user. As an occasional compiler hacker it's been quite nice though.
I guessed correctly that it was using uninitialized memory, and errored when that wasn't zeroed out, but my C wasn't good enough to find where. Had this been Julia the whole C code would have been Julia code and I would have had a chance to dive in and debug. I ended up having to get a colleague who's fluent in C to help.
And then claimed that this was somehow better in Python + C, which is not my experience. I expect this to be easier in Julia than in Python and C.
I totally agree that the tooling is not where it needs to be, btw, but now that the target has stopped moving I expect it to get there soon.
This only works (easily) as long as you don't have user-defined types
> If all the effort gone into Julia had instead been spent on fixing remaining warts in Python workflow for science, we wouldn’t even havee this conversation.
Python is too dynamic, you cannot just fix remaining warts. From Julia documentation I know that Julia language has been designed for speed and e.g. some dynamic possibilities have been omitted in order to be able to generate fast code. For a general idea about Julia speed see e.g. this 7 hours old excellent Juliacon video: https://www.youtube.com/watch?v=XWIZ_dCO6X8
Python certainly works. But already for syntax alone, if you have written Julia, it's hard to - in my case - go back to R.
https://numba.pydata.org/numba-doc/dev/user/jitclass.html
No one intends to fix Python but it’s straightforward to do things like Numba: use a decorator to read out the AST for a function, reimplement it however you like and pass back the compiled function, and document the semantics.
http://www.stochasticlifestyle.com/why-numba-and-cython-are-...
So you can work with types even when you've never seen their definition given how the compilation will occur with all of the pieces together instead of separately.
Python is rather a mess. Code written in Python can't be sped up without pain/cost, and apparently it will never support concurrency natively. It also suffers from the bane of weakly typed languages, errors at run time instead of compile time.
I think the sweet spot for a language with most of Python's benefits that fixes many of its glaring warts is enormous.
It’s not weakly types either: you can’t add a list to a string. It’s dynamically typed.
For package developers it is a lot eaiser to use Julia.
Saying one should focus more on Python and we would not have these problems is missing the point. Enormous resources by countless companies has been poured in to solve the performance problems of Python.
It is almost impossible to do due to the language design of Python. You cannot fix it without breaking the language.
Julia in contrast required minimum effort and resources to get fast. It is almost a toy project compare to Python. It is all down to clever language design which allowed them to use a rather dumb and simple compiler while letting LLVM do most of the heavy lifting.
Every advance of Python is going to require 10x the effort of advancing Julia. It will just be a question of time before Julia catches up. Python has a huge lead so that will still probably take years but it will happen.
Python’s design leaves something to be desired performance-wise for those coming from JVM or native languages, but it’s a trade off, not an obvious win (for Julia), and the problem goes away as programmers get wise to performance strategies in Python.
The library is for reverse-mode automatic differentiation, but let's put AD itself aside and talk about code generation. As an input to code generator, I have a computational graph (or "tape") - a list of functions connecting input and intermediate variables. As an output I want a compiled function for CPU/GPU. (Note: Theano used to do exactly this, but that's a separate huge system not relevant no Numba or Cython).
In Julia I follow the following steps:
1. Convert all operations on the tape to expressions (~approx 1 line per operation type).
2. Eliminate common subexpressions.
3. Fuse broadcasting, e.g. rewrite:
c .= a .* b
e .= c .+ d
into e .= a .* b .+ d
Dot near operations means that they are applied elementwise without creating intermediate arrays. On CPU, Julia compiler / LLVM then generates code that reads and writes to memory exactly once (unlike e.g. what you would get with several separate operations on numpy arrays). On GPU, CUDAnative generates a single CUDA kernel which on my tests is ~1.5 times faster then several separate kernels. Note that `.=` also means that the result of operation is directly written to a (buffered) destination, so it no memory is allocated in the hot loop.4. Rewrite everything I can into in-place operations. Notably, matrix multiplication `A * B` is replaced with BLAS/CUBLAS alternative.
5. Add to the expression function header, buffers and JIT-compile the result.
In Python, I imagine using `ast` module for code parsing and transformations like common expression elimination (how hard it would be?). Perhaps, Numba can be used to compile Python code to fast CPU and GPU code, but does it fit with AST? Also, do Numba or Cython do optimizations like broadcasting and kernel fusion? I'd love to see side-by-side comparison of capabilities in such a scenario!
I'm fairly certain the steps you've listed can be accomplished through AST manipulations, and would go something like
def manip_ast(fn):
import ast, inspect
fn_ast = ast.parse(inspect.getsource(fn))
new_fn_ast = …
return compile(new_fn_ast, …)
def rewrite(fn):
fn = manip_ast(fn)
fn = numba.jit(fn)
@rewrite
def func(*args):
…
there's nothing in the language that prevents this from working with the autograd package, except no one's taken the time to implement it (https://github.com/HIPS/autograd/issues/47). That said, for many tasks with wide vector data, a DL framework is going to do ok, e.g. PyTorch.> Julia compiler / LLVM then generates code that reads and writes to memory exactly once (unlike e.g. what you would get with several separate operations on numpy arrays)
Numba's gufuncs address exactly this + broadcasting over arbitrary input shapes. I've used this extensively. That said, I don't find fusing broadcasting is always a win, especially when arrays exceed cache size. Numba's CUDA support will also fuse jit functions into a single kernel, or generate device functions.
Sometimes you want manual control over kernel fusion, and I've found the Loopy (https://documen.tician.de/loopy/) to be fairly flexible in this regard, but it's a completely different approach compared to Numba/Julia.
I'd be interested in a side by side comparison as well, and I was thinking that the main difficulty would be that I couldn't write good Julia code, but maybe we can pair up, if that'd be interesting, to address several common topics that come up (fusion, broadcasting, generics but specialization, etc).
I believe it's more complicated than most posters there realize, especially in the context of PyTorch (which uses a fork of autograd under the hood) with its dynamic graphs... Anyway, AD deserves its own discussion, that's I didn't want to concentrate on it.
> I'd be interested in a side by side comparison as well, and I was thinking that the main difficulty would be that I couldn't write good Julia code, but maybe we can pair up, if that'd be interesting, to address several common topics that come up (fusion, broadcasting, generics but specialization, etc).
Sounds good! Do you have a task at hand that would involve all the topics and could be implemented in limited time? Maybe some kind of Monte Carlo simulation or Gibbs sampling to get started?
Can you elaborate on how Julia represents arrays of unions, or point to some documentation? I'm working on something where an automatic efficient representation would be useful, and I'd like to learn from other's experience.
In terms of the Julia changes that were needed to support this, there were two key PRs:
https://github.com/JuliaLang/julia/pull/20593 https://github.com/JuliaLang/julia/pull/22441
https://youtu.be/1jN5wKvN-Uk?t=1h3m
It was fun to do this with everyone at JuliaCon and online, and thought it was worthwhile to share here.
For example, I have downloaded julia-1.0.0. I try to follow this tutorial here, linked in this post by someone: http://juliadb.org/latest/manual/tutorial.html
Then I do this and get an error:
julia> using JuliaDB [ Info: Precompiling JuliaDB [a93385a2-3734-596a-9a66-3cfbb77141e6] ERROR: LoadError: UndefVarError: start not defined Stacktrace:
Every time. Even the screenshot of Julia code that julialang.org used to have was not runnable per admission of core devs.
What am I doing wrong? How are you able to run large Julia programs successfully?
Edit:
Let's try tutorial at https://www.analyticsvidhya.com/blog/2017/10/comprehensive-t.... First command: Pkg.add("IJulia"): command fails to install dependency Conda.
Same for the tutorial at http://ucidatascienceinitiative.github.io/IntroToJulia/Html/...
Sigh. Give up
That would help a lot of people get started.
You should have tried 0.4.2, 0.6.3, 1.0.1. Motto: Don't start too fast and be slow giving up ;-)
Which package declared support for 1.0.0 and didn’t compile?
None of the packages work with it yet. I guess I could go find an older version, but it seems like a problem that Julia will happily allow you to install a package it isn’t compatible with. What’s the point of the Pkg system then? CRAN’s model makes a lot more sense.
https://github.com/JuliaComputing/JuliaDB.jl/commit/8bf3057d...
A automatic check as in R would indeed a better choice.
More informative wikipedia page: https://en.wikipedia.org/wiki/R_(programming_language)#CRAN
That being said, R was GNU S once upon a time, so they didn't really build a new language, rather an open source clone of a popular proprietary tool. (In case it wasn't clear I both love R and am madly excited that Julia has finally hit 1.0.0).
The Julia package ecosystem is much more anarchic, like npm. You basically just have to have a public git repository with certain files in place, and a cursory review from the managers of the package metadata.
There are tradeoffs. I'm honestly not sure which one is the right way. I really appreciate how much I can trust that a CRAN package works, but there's a reason so many R devs are using devtools to do an end-run around it.
One thing I found is that types in Julia are first-class values [0]. You can put them in variables, pass them around, inspect them, even produce new ones in runtime. Opens up all kinds of metaprogramming opportunities. Very Lispy! (Well, Julia is Lispy). It's also interesting that types are optional, yet they're significant for the optimising compiler. Like, if you do type the arguments of a function, the optimising compiler will have less work to do.
It also looks like much of the compiler pipeline is exposed to the user [1]. You have access to the parser, can tweak the AST. Macros also there of course.
[0] https://docs.julialang.org/en/stable/manual/types/ [1] https://docs.julialang.org/en/stable/manual/metaprogramming/
Giving types for function arguments doesn't actually have any effect on performance: the compiler specializes on concrete runtime argument types anyway, so completely untyped code is just as fast as fully type annotated code—since the types are known when the code is compiled. On the other hand, giving type information is essential for performance involving memory locations, i.e. field types and the element types of collections.
I hope Julia doesn't rely on inferior libraries just out of copyleft phobia. I would much rather use FFTW than FFTPACK or whatever other alternative they have in mind. FFTW is really best in class.
I'm okay with them making FFTW optional, but please make it opt-out, not opt-in. People should be getting the best software by default. Copyleft isn't going to hurt anyone but people who are trying to hide source code, and scientific computing needs all of the visible source code we can get.
Julia's Base is for the language, not for every little detail so this is all handled by the package ecosystem.
FFTW is available in a package under the MIT license [1]. Also its author is a top contributor to Julia ;).
> Note that FFTW is licensed under GPLv2 or higher (see its license file), but the bindings to the library in this package, FFTW.jl, are licensed under MIT. This means that code using the FFTW library via the FFTW.jl bindings is subject to FFTW's licensing terms.
If you have an idea on how to make that clearer, we would be happy to review a PR to the FFTW.jl repository.
Their type system is pretty interesting, and allows for some really cool abilities to parameterize things using types. I'd like to have seen more work done on, effectively, strong typedefs (or whatever $lang wants to call them). However that sort of thing is fairly uncommon so it's hard to hold it against them too much.
The biggest issue, and one they seem unwilling to really address, is that actually using the type system to do anything cool requires you to rely entirely on documentation which may or may not exist (or be up-to-date).
Each type has an entirely implicit interface which must be implemented. There is no syntax to mark which methods must be present for a type. No marker for the method definitions, no block where they must be named, or anything like that. You can't even assume you'll find all the interface methods defined in a single file because they can appear literally anywhere.
Whoever wrote the type has in mind some interface, a minimal set of methods, that must be present for any derived type. There are only two possible ways to determine this. The first is to look to the documentation. Even for the basic types defined by Julia this documentation doesn't seem to exist for all types. I don't have high hopes for most libraries to provide, and keep up to date, this documentation either. This concern gets even greater when considering the focus is largely on scientific computing.
Without up-to-date documentation, the only option is to manually review every file in a library and keep track of the methods defined for the type you're interested in. With multiple dispatch, you can't even get away with just checking the first parameter either. Then you need to look at the definitions for those methods to narrow your list down to the minimal set required. This is not an easy task.
This issue has been brought up before and discussed, but nobody seemed very interested in it. This is a fairly major issue in my view, as it cripples the otherwise very interesting type system. As it stands, it seems to be a fairly complex solution to the issue of getting good compiled code out of a dynamic language. It could be so much more.
`methodswith` tells you all methods that been defined on a type. Since it returns an array of methods you can do some more introspection on that, if you're so inclined.
I've also just written a few test cases using the interface I'd like, then worked on methods until my tests pass (which can be just not throwing MethodErrors).
As a comparison, consider that the C++ standards body has been working on, debating and serially rejecting the various "C++ concepts" proposals for years now.
If you want to get fancier, yeah, it gets hard. But something as simple as a syntax block that lists the interface methods would be enough for now.
The fancier stuff is already projected so far out to at least 2.0 that I don't understand why a simple working solution wouldn't be desired for now. It would also simplify the work of changing code later to work with 2.0.
I really don't understand how this issue wasn't addressed a long time ago, and why it wasn't a blocker for 1.0.
The main reason this has not been implemented as a language feature is that people are worried about settling on a design that would be impossible to make fast and concise. It is certainly on the designers' radar, and was discussed specifically at JuliaCon 2017 in Jeff Bezanson's talk.
There are some people who plan to attempt a trait system as a package on Julia 1.0. Perhaps this will be successful and we won't need language changes! Stay tuned.
As an aside, I wouldn't take the lack of action as lack of interest. People are interested, but it wasn't prioritized yet. It will get effort and attention!
First, this extends deep into the core of Julia, and I don't see how a traits package would be involved with that.
Second, and this dovetails with the first issue, this needs to be something that people actually use as a default action. Part of that means ensuring the built-in types make use of this.
Related to all of this, I worry it's too late to really make a meaningful change here. The culture and existing packages are already set without it. Adding the feature as a requirement isn't going to happen anytime soon unless people are willing to continue to break things post 1.0. And it needs to be a requirement or it won't get used except by people that will already provide documentation.
The lack of interest I mentioned mainly came from some github issues which either received little attention, had creators that had a very "maybe it would be nice if" attitude, and responses that were questioning the benefit compared to the cost of implementing the syntax changes.
No, Tim Holy described over dinner how all that was necessary to complete the existing traits packages was method deletion, and that's in v1.0.
But as an external package it still has the issues I described as not being part of the defaults of the culture. Without that it just becomes a nice-to-have that only people serious about writing good quality, usable code are going to use. And these are the people most likely to have good documentation in any case.
I would also bet (but not too much) that we eventually see major progress in removing the GIL. I really don’t think it’ll be around forever!
One starts with quick and dirty solution, makes it work on a small dataset and then struggle to make it utilize at least 4 cores to cut running time with more realist datasets. Surelly I can code numerical calculations in C++, but then the code cannot be maintained by python-only guy. So I hope that Julia or anything else with better parallel support replaces Python for scientific calculations when scaling quick and dirty solutions is straightforward.
[1] http://scipy-cookbook.readthedocs.io/items/ParallelProgrammi...
I think this is so badly needed to get an easy route to pipeline paralellism, which is simply everywhere in today's data analysis "pipelines".
https://docs.julialang.org/en/latest/base/multi-threading/ https://docs.julialang.org/en/latest/manual/parallel-computi...
It's listed as experimental since in a 1.x released it's planned to be changed to work on top of the task interface.
> [...] it may change for future Julia versions, as it is intended to make it possible to run up to N Tasks on M Process, aka M:N Threading
M:N threading is (I think) the same as the "multiplexing" I mentioned. Have seen it called "M:N multiplexing" before.
(At the very end of https://docs.julialang.org/en/latest/manual/parallel-computi...)
* Julia has had tasks/co-routines basically forever and uses them for all blocking operations so that no explicit non-blocking I/O or callbacks are required.
* It also supports multithreading using the @threads macro.
* However, it does not yet map tasks to threads, but that is very close to ready: https://github.com/JuliaLang/julia/pull/22631.
We expect this work to be finished in a near-future 1.x release and then Julia will support M:N multiplexing.
It's multi threading model is mostly multi process. Mostly because people write code to run on clusters on machines, not just single ones.
co-routines, channels, etc will be nice, and I expect not too far away.
I am ambivalent over whether it has multiplexing or not. When I need parallelism I fall back to OS threads if available or processes. One or the other is usually always a first class citizen. Whats not that common, are real lightweight coroutines.... I want my laptop to be able to simulate the transport layer over a sizeable portion of the internet, while I watch youtube.
Very few pieces of software combine simplicity and power. I'm very excited.
It'd be great if Julia devs could help deploying Julia Pkgs on Nix, now that Julia is getting ready for prime time and Nix is steadily gaining more and more users: https://github.com/NixOS/nixpkgs/issues/20649
This makes a poor first day impression for new users who expect Plots.jl, IPython.jl, Juno.jl or other prominent packages in 1.0. The package maintainers are scrambling to catch up. I recommend using 0.7 for a couple weeks until the community catches up unless you are proficient enough to contribute PRs and help out.
(edit) To be clear, 0.7 was released at the same time as 1.0.
And perhaps more importantly, what is missing?
I'll give you one quick example of the design. Images are just arrays of "Colors". The upside is that you can write generic code that handles arrays of Colors of any kind, meaning you don't have to worry about iterating over the color axis. You also don't have to worry about whether your 100x100x3 array is three images (at different times or locations) or one image with three RGB channels. All of this comes at little to no cost in terms of speed.
Check the docs here: http://juliaimages.github.io/latest/
But fair warning, it still needs some fixes for 0.7/1.0. We're on it!
With respect to your question about what’s missing: Coming from Python, I think one might be surprised by how much less one feels the need to rely on packages in Julia. The language has some very powerful abstractions that really makes one rely less on packages than you’d expect.
Julia has really inhereted a lot of great lessons from Lisp and that’s made the language an absolute treat for ‘rolling your own’. Meanwhile, some lessons from more modern languages have also made Julia much more effective at sharing your abstractions with others much easier and effective than any Lisp I’ve ever seen.
Julia v0.6 was already very functional/usable release but having 1.0 allow me to write code and have confidence that it will continue working in the same, solid foundation in the years ahead.
I started my coding career over 25 years ago and I had done much work in C, Java, JavaScript, and PHP. Julia is the best language that I have worked with so far. Fast, dynamic, and highly productive!
I'll be moving to 1.0 release when my dependent packages are upgraded to support 1.0.
$ time ipython -c '1+1' Out[1]: 2
real 0m0.493s user 0m0.401s sys 0m0.063s
$ time julia -e '1+1'
real 0m0.222s user 0m0.115s sys 0m0.101s
Yes, python itself is still much faster. I compare against ipython just because that used to be my go-to interactive shell for technical computing.
The real question is how long does `using Plots` take in v1.0?
Julia is not only good for its performance, but also its multiple dispatch, its type system and more. Because of those features we have https://github.com/JuliaGPU/CUDAnative.jl, more elegant package interface, like https://github.com/JuliaOpt/JuMP.jl and there is more.
I still believe there is no silver bullet, if you want something Python then you get another Python. But think about this: is the full Python's dynamic feature really what we need in works like simulation, HPC, etc.? Probably no. I think Julia is kinda of balance for the related field (like for scientific computing). Just like in the old days, people start using FORTRAN, MATLAB, Lisp as the most advanced tools at that time. We start using Julia now.
I'm not an PL expert, so I would like to ref a previous discuss in discourse, which is more detailed
https://discourse.julialang.org/t/julia-motivation-why-weren...
I was just trying to say that if you want to develop something with reasonable performance, elegant interface in a short time. Probably under this situation, Julia is more suitable.
The point is mostly to (a) show the difference between fast languages compiled to efficient assembly and (b) represent code someone new to a language may bang out to get something done (while avoiding performance pitfalls) and avoid the benchmark game.
That said, I agree. I try (poorly) not to advertise speed, because people coming from languages like R will rarely fail to write type unstable code that is slow, observe JIT compilations that make Julia a little laggy, and then come away disappointed.
Things like the type system and meta-programming shown off in your examples are amazing, and also not something you can reproduce in other languages by adding binary dependencies.
?
> (d)evolves into a lookup table
We can make the arbitrary decision not to accept that, and instead try to use our best judgement on what optimizations to accept.
"One can, with sufficient effort, essentially write C code in Haskell using various unsafe primitives. We would argue that this is not true to the spirit and goals of Haskell, and we have attempted in this paper to remain within the space of "reasonably idiomatic" Haskell. However, we have made abundant use of strictness annotations, explicit strictness, and unboxed vectors. We have, more controversially perhaps, used unsafe array subscripting in places. Are our choices reasonable?"
http://www.leafpetersen.com/leaf/publications/ifl2013/haskel...
Perhaps because the Julia homepage distracts them :-)
"Julia is fast! Julia was designed from the beginning for high performance."
I think I started using Julia around 2013 or so after getting fed up with the slower speed of octave, so I'm guessing that things should be a fair bit smoother from this point on if they've officially tagged 1.0.
But for classical scientific computing, of the sort typically done by real engineers wearing coke-bottle glasses, using MATLAB and expensive toolboxes, it can't be beat. It's really easy to work with matrices and make your code super-fast. And it steals all the good parts of MATLAB syntax but with a good, modern, sensibly designed programming language.
Also, DifferentialEquations.jl is undeniably the best available piece of software for solving differential equations, in terms of both performance and ease-of-use. It feels like using alien technology or something.
As a more general point though, I work in an academic environment and I see tons of languages whizzing by. I routinely see C, C++, Python, R, Matlab, csh, bash, and the list just goes on and on. I've even seen someone write a command line tool in PHP that had no justifiable reason for being written in PHP. But it works, so whatever. If it was written in Julia, maybe it would be uncommon, but also slightly more refreshing.
So, really, why not Julia?
One-based indexing has a good, long history.
# Magic
julia> Base.to_indices(A, inds, I::Tuple{Complex, Vararg{Any}}) = (real(I[1]), imag(I[1]), to_indices(A, Base._maybetail(Base._maybetail(inds)), Base.tail(I))...)
julia> A
4×5 reshape(::UnitRange{Int64}, 4, 5) with eltype Int64:
1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20
julia> A[3 + 2im]
7
```When Octave was getting a lot of traffic from the first Coursera machine learning class, when it was just Andrew Ng doing a course and before the Coursera org existed, we were getting a lot of novice users who would do something like
some_matrix(i,j) = 5
and get cryptic errors about how matrices cannot be indexed by complex numbers. This is because by default, in Matlab and Octave `i` and `j` are functions that evaluate to the imaginary unit. You have to overwrite the function names with `i=2, j=3` or whatever beforehand, which novices often forget. This was happening often enough that I pushed some patches to warn, "did you forget to assign i or j?" if someone tried to index matrices with complex numbers.Point is, people in Matlab and Octave often unintentionally try to index by complex numbers.
0-based indexes are commonplace in programming circles because of the C language, however 1-based indexes are the earlier standard set by Fortran.
EDIT: If Julia becomes popular outside data and numerics circles, it will have pulled off nothing short of a miracle in getting people to adopt 1-based indexes. This feud is older than vi vs. emacs. :-)
It will for sure hurt adoption.
They're all 0-based (with maybe R as an exception, but R is a niche language. If that's what Julia aims to remain -- their loss).
The folks who designed these languages knew how not to alienate their future market.
Strongly disagree.
I can move from C to Python without a second thought and rewrite code from one to the other without even thinking.
Having to rewrite an algorithm working on multidimensional arrays that was written with 0-based conventions to 1-based conventions and have the resulting code remain readable is a freaking headache.
Compilers are pretty good about optimizing integer constants.
My reaction:
- the default is 1-based, which means the bulk of Julia code will adopt the convention and therefore the vast majority of coders in 2018 will have to do mental gymnastics to understand what the code does.
- when I read Julia code, I will never know which convention the code was written with unless I dig to find where that particular flag is set.
- passing 0-based arrays to a library routine that expects 1-based stuff, what happens then?
- for code that will be 0-based and uses library code that expects 1-based (assuming that's possible without paying copying overhead) will force the code to mentally switch between the two modes. Ugh.
In short: offering is a choice is maybe even worse than enforcing 1-based.For concern 2, it's as easy as finding the type. And without knowing the types you wouldn't know what the code did anyway.
For concern 3, a type error.
And for 4, because it's a type there is zero runtime overhead. A view of the array with the offset the code expects is constructed, often automatically based on the types involved. This view is often a zero cost abstraction at runtime because of how Julia specialization works. So at worst you pay some (extremely minor) compile-time/load-time costs.
1 based indexes were just fine.
Zero based is much more sane. If the array is regarded as being made up of larger groups of elements, say groups of 8, then ⌊index/8⌋ gives us the group and group x 8 gives us the base element of group. Not so if index is one-based.
Zero based multi-dimensional coordinates are easy to convert to a flat address. E.g. 3D case: just ABz + By + x.
Imagine distances were one based (so that either 1 m or 1 km means no displacement), and then trying to convert a given distance between m and km. Yikes!
Music intervals are one-based, to their great detriment. We end up with a "rule of nine" for interval inversion and that comes from the octave of the diatonic scale having seven notes!
One-based indexing is okay when the indices don't have a strong numeric meaning (beyond basic successor/predecessor relationships), or none at all (basically are symbolic and could be replaced by any set that can be enumerated by the natural numbers).
As soon as the index domain is involved in displacement calculations that feature multiplication and division, anything but zero based is disadvantaged.
In Algol based languages indexes are customizable, I don't remember ever writing array [0..9] of Integer instead of array [1..10] of Integer.
C, C++ and their descendants took over and I just had to adapt.
It's just a wanton complication, like insisting on Roman numerals instead of a radix enumeration.
Reminds me of the complaints my friends had about Linux when I first showed them a fantastic window manager.
"This will never get anywhere. It has no Start button."
If I were in a team that refused a language just because it is 1 based indexing, I would really worry about the abilities of the team.
When I do sigmas in math, they go: i=0,i<n and very rarely i=1, i<=n unless the problem really is made simpler by the weird 1-based indices.
guilty as charged :)
It's also not hard to get used to. No more OB1 errors.
That makes no sense. Neither 1-based indexing or 0-based indexing will save you from OB1 errors.
As a matter of fact, if you make more OB1 errors in a 0-based indexing language, it's probably because your brain is wired to think 1-based.
The problem: there are legions of programmers whose brain is wired to think 0-based and are guaranteed to suffer through a lot more OB1 errors if they try to adopt Julia.
0 based indexing is not because of C's influence. It's because that's how computers work. ASM is 0-based indexing. The first memory cell on a computer doesn't start at address 1.
If you are calculating an index the domain and range of the index function are very often 0-based. So you have to subtract and add one to convert from and to 1-based. It's just messy.
- Julia is by far my favorite language. (I've also written significant code in Java, C++, and Matlab and small projects in Python, Mathematica, R.)
- Julia is my favorite because it is super expressive but also fast. You don't have to make (big) compromises. There's a great blog post called "Why We Created Julia" with the punchline "we are greedy." [1] 6 and a half years later, it holds up well.
- In Julia, nothing hurts. There are so many little quality of life improvements that add up to more than just quality of life. Some are small, like multiple assignment (x, y = lst[1], lst[2]). Others are more conceptual, like well-supported first-class functions (that are also fast). Another example: you're not forced to write code in an arcane style or with special libraries to get speed. Your normal for-loop or vectorized code or functional code will all compile to something efficient.
- Because Julia is fast and expressive and extensible, in Julia everything can be Julia and not a mashup of other languages. I've been doing some work in Python recently, and it's painful to have Python lists, numpy arrays, Pandas series, and so forth. Converting between types isn't that hard, but it's real mental (and textual) overhead which just doesn't have to be dealt with in Julia.
- Yes, Julia has 1-based indexing by default. There are packages for custom array indices (including 0-based, symmetric around 0, pick your favorite) which are, surprise, super performant and easy to use. It seems uncontroversial to me that for some cases 1-based indexing is a more natural mental model and for some cases 0-based is more natural. When it matters a lot, you can pick your indexing. When it doesn't matter much, which is most of the time, it doesn't matter. Julia catches a shocking amount of flak for this...if the worst thing about a language is that it sometimes makes you add or subtract 1, you must really like that language :)
- The Julia package ecosystem is young and evolving. It has some standouts such as DiffEq (differential equations) and JuMP (optimization modeling language) which are, to my knowledge, best-in-class in any language. I'd say the modal experience is more like DataFrames: already super functional and productive, not yet as full-featured as the <popular language>-equivalent, and slowly evolving towards something better than the popular language equivalent. E.g. DataFrames is just a wrapper around Julia lists which makes it much lighter weight / easier to understand / easy to interop with than Pandas.
- There are some growing pains around a young-ish language which, until today, hadn't reached its first stable version. Presumably those will taper off now that we're at 1.0, but it'd be a lie to say there aren't any.
- My first open source contributions, modest as they are, are all in Julia. Pre-Julia I never knew how to get started, but Julia makes it easy to transition between user and developer.
This is exactly what I like about Julia. Even though getting started in Julia was tough (esp the older versions), once you get off the ground Python starts to seem like a very hard language, in that Python requires you to use these special libraries to run fast code. In Julia, the most obvious way to do something (e.g., for loops) is perfectly fine.
The main thing Julia lacks, for me, is an equivalent to Pandas. The DataFrames library lacks many very useful features of Pandas. But I am sure someone will tackle that.
The #1 requirement I have is the ability to make binaries for some program. You can compile a C program and get a binary. There's no practical equivalent for Julia at the moment and I think this limits its production potential.
Not had chance to how nicely PackageCompiler.jl works but am hopeful :)
One of the reasons I used Julia in academia was slurm manager ;)
then in computer, (assembly) you will request an element by saying for example base_offset+(1elem_len). that would make it logical to use 0 as an offset, because then you can use 'nelem_len' as a generic number to incrememnt the offset by to select array elements.
thus for a computer and how it functions, 0-based array would make more sense, and any other thing, would just be some overlay over how a computer works just to make it more human readable....
computers dont care for what is first idex, it will always equate to base+n*elem_len to have to find the actual location in memory...
if people have been discussing that for decades and what is better, it's just another example of people not understanding what is an opinion,and what is objectivley true... none is better, your compiler is taking care of business, really it is, and no longer like it was 1999... they have been patched many times!
And what about for a human and how it functions? Are humans here to make computers' lives easier or vice versa?
Humans think from 1...N inclusive and this is the source of a litany of bugs when users first learn a language.
And what you described in asm is just one implementation. In fact, the array documentation says Julia doesn't guarantee tight packing so it doesn't even apply. The next element could be anywhere, and in fact wont be in the case of heterogeneous arrays. Asm is also not straightforward. For instance zeroing a register isn't done with the mov instruction but xor
"computers dont care for what is first idex...none is better, your compiler is taking care of business"
I think you're assuming everyone thinks the way you do. I assure you that's not the case. There are legions of people (and not just programmers, math folks do it too) who don't "think from 1 to N"
I think that is the key. "1, 2, 3. There are 3 apples." is a little easier than "0, 1, 2. There are 3 apples." Counting the first item as "1" total items leads to 1 being more natural in the same way that incrementing a pointer by 0 giving you the first element is more natural.
The former is the abstraction layer most people are more familiar with.
You're right ... sporting events ... how could I have not included that very scholarly pursuit of engineers, scientists and programmers in my analysis.
I'm not trying to claim that 0-based is better than 1-based. I'm just trying to point out that outside of the fairly limited crowd who spend their workday in things like Matlab and R, the vast majority of coders in the world in 2018 are working in 0-based indices languages.
If Julia is a worthy language which aims to attract a crowd beyond the niche R/Matlab folks, then choosing 1-based indices is poor tactics.
Furthermore, the widespread mathematical / scientific computing languages have used 1-based from FORTRAN through Matlab and Mathematica. Statistical papers are published with accompanying R code , very rarely with Python. If 1-based indexing is too hard to get used to, you may not be in the target audience. Anecdotally, I used C and Python well before started R, and I’m not really the smartest bulb in the box. I was annoyed for about a week. If you have the knowledge of what you will use Julia for, this hurdle seems very minor in my opinion.
https://onlinelibrary.wiley.com/doi/abs/10.1111/j.2044-835X....
Two studies are reported investigating children's conceptions of the number zero. The first, with 31/2–61/2‐year‐olds, charts preschoolers' understanding that zero is a number among other numbers with its own unique value, namely nothing. Children's achievement of this understanding occurred in three phases. At each phase understanding of zero lagged behind comparable understanding of other small numbers. The second study, with 51/2–10‐year‐olds, investigated children's developing conception of simple algebraic rules, such as a + 0 = a. Results showed that even the younger children had some understanding of several algebraic rules. The older children had acquired more such knowledge, but at all ages algebraic understanding was advanced for rules pertaining to zero, in comparison to those pertaining to other small numbers. These results suggest that zero plays a special role in children's increasingly algebraic knowledge of number. We conclude that since zero is difficult to conceive of and use originally (Expt 1) children develop special rules for its use, and that this provides a first step towards their formulation of more general algebraic rules (Expt 2) and towards an expanded conception of number and mathematics.
As for "a computer" - unless your actually writing code for memory allocation - you are (should be) writing to an abstract machine anyway (trivially, if you want to sum numbers in a dense 10^6 by 10^6 matrix - wouldn't it be nice to utilise 1024 cores if they are available? Unless you have side effects happening, do you really care about the order of additions? And if the numbers are 256 bit integers - do you want to care if they're stored big or little endian, in 8 or 16 bit words? In two's complement?).
It seems that the first step in getting Jupyter to know about a new version of Julia is to do Pkg.add("IJulia") in Julia. Except that that doesn't work; it seems that now you're supposed to use some special pkg mode in the Julia REPL.
So, I hit ] to enter pkg mode and type "add IJulia", which seems to be the appropriate thing. It churns a bit, tries to build something called "Conda" (which is apparently the dependency-management bit of Anaconda, the Python distribution thing), and gives me an error message that starts like this: "ERROR: LoadError: ArgumentError: isdefined: too few arguments (expected 2)" followed by a stack trace whose first and last entries are "top-level scope at none:0", which doesn't exactly help to nail down where the problem is.
Related operations like "build Conda" and "build IJulia" give similarly unhelpful error messages (some of them enjoining me to do things like Pkg.build("Conda") that so far as I can tell don't actually work at all).
Do I just need to wait for release 1.0.1, or is it likely that I've done (or left undone) some unfortunate thing, that I could fix and make everything work?
I thought it might be interesting to try the Juno IDE, but met with a similar lack of success: first of all it told me I needed to do Pkg.add("Atom"); when I had done (not that but) the approximately equivalent ]add Atom, starting Juno yielded only a cascade of error messages (no method matching eval(::Module, ::Expr); failed to precompile Media; failed to precompile Juno; failed to precompile Atom).
Presumably, again, the answer is to wait a little for things to settle down. It feels as if it might have been better to get all the ducks in a row before declaring version 1.0, though...
Of course, more time would have been nice, but you can always find a reason for a delay. At some point you have to rip off the bandaid.
It will take awhile for it to fully utilize v1.0. Remember, it just came out so packages will need to catch up. The debugger for example needs to be updated, along with the plotting packages and such. But once everyone updates it should give a very similar experience.
The best documentation I can find is this article, but it seems a bit spartan on how-to: https://medium.com/@sdanisch/compiling-julia-binaries-ddd6d4...
So while you might find the language itself pleasant, you'll probably find a lack of libraries.
(personally I'm a fan of strong static type systems (e.g. haskell), and I think it is a shame the designers didn't go down that path. But hey, they did all the work and not me, so who am I to complain. Kudos to the 1.0 milestone!)
In julia, you can write a custom type, and immediately have access to all of the builtin libraries.
For example, I wrote a drop-in replacement for floating points, and immediately had complex numbers, matrix math, gaussian elimination, fourier transform, etc... And could rapidly compare the numerical performance of that with fp.
For a more exotic example, I wrote a galois field GF256 type and was immediately able to reed-solomon encoding and decoding using the matrix multiplication and matrix solving libraries.
For an even more exotic example, I wrote a "lexical GF256" type (basically passing strings) and had the system generate optimized C code for reed-solomon decoding, using the builtin julia matrix solver without having to manually do matrix solving for every possible (n,k) reed-solomon system.
It was also relatively easy to write a verilog generator (~3 days of work), so you could pass bit-arrays representing wires, run you unit and property tests on the binary representation, then redispatch the function passing lexical types and get verilog code out the other end, then transpile the verilog to C using verilator, dynamically load the verilog into the julia runtime, and then unit and property tests on the transpiled verilog.
I'm sure it's possible to do this in haskell, but I imagine it would be harder.
To get good performance with a static type system, you need generics. Which means longer compile times, as the libraries you use must be compiled as part of your project so that the code specialized for the types you're calling the library with is generated. You can't have piles of native code lying around just waiting to be used (like in the C world, for example).
But this is similar to how Julia does it at runtime, with the multi-methods being JIT-compiled for the types you're calling them with. So either way you pay the cost somehow (and yes, there are ways to reduce that overhead, of course).
If you're expecting a high-performance jit that needs to be called all the time because your program gets unloaded from memory and needs to be rebuilt by a kubernetes server every fifth web request, don't use julia.
I've been working on updating a wide variety of engineering software apis/wrappers and a large informatics library for .7/1.0, but I'd love to hear any suggestions of must-need technical packages that users want from MATLAB, Python, or R.
> square(x) = x * x
> @code_typed square(2.0)
CodeInfo(
1 1 ─ %1 = Base.mul_float(%%x, %%x)::Float64
└── return %1
) => Float64
> @code_typed square(2)
CodeInfo(
1 1 ─ %1 = Base.mul_int(%%x, %%x)::Int64
└── return %1
) => Int64
Functions specialize automatically to the arguments you pass in. julia> square(x) = x^2
square (generic function with 1 method)
julia> @code_native square(1)
.text
Filename: REPL[3]
pushq %rbp
movq %rsp, %rbp
Source line: 1
imulq %rdi, %rdi
movq %rdi, %rax
popq %rbp
retq
julia> @code_native square(1.0)
.text
Filename: REPL[3]
pushq %rbp
movq %rsp, %rbp
Source line: 1
mulsd %xmm0, %xmm0
popq %rbp
retqI strongly prefer the dependency management system to python's efforts.
I like the strong arithmetic precision management facilities.
Dynamic programming is interesting, but I'm not sure that its great for collaboration.
Sounds like 1.0 hasn't propagated everywhere yet :-)
https://gist.github.com/ontouchstart/5222903bf4a2d040e6eee6a...
… they forgot to compare Julia to JavaScript — the most popular programming language in the world.
Or is that because authors don't want Julia to be used by anyone?
¯\_(ツ)_/¯
[I'm joking, ofc. But I still find it weird how almost every modern language is mentioned, but JavaScript…]
I know it is not necessary, I was just curious, as thr language seems to have a lot of metaprogramming options, and Femto-Lisp being part of the compiler.
Is TCO on the roadmap, or is the "mess it does with stack traces" too much?
https://github.com/chrisvoncsefalvay/learn-julia-the-hard-wa...
No, it's the same indication, twice. Once you've decided on 1-based, you almost definitely want to include both endpoints. Else you end up saying things like a[1:n+1] to indicate "the whole array please", which is annoying.