Some things I don't like about Julia:
- array.mean().round(digits=2) is more readable than round(mean(array), digits=2)
- Poor support for OOP (no, pure FP is not the optimal programming approach).
array |> mean |> x->round(x; digits=2)
will do what you want and is arguably more readable than dot notation.Not supporting OOP is very much intentional. It has structs and multiple dispatch (which has proven to be insanely useful in Julia's domain of scientific computing) so about the only thing you lose is the namespacing that dot syntax facilitates.
Any language that has Uniform Function Call Syntax (UFCS) [1] lets you replace wrapped function calls with linear calls like that, it's clearly easier to read, and much easier to write, specially when you consider programmers' tooling (in case it's not obvious why: you can type the name of a symbol and a '.' and the IDE shows you the available functions... this works also for free functions that take the symbol's type as the first argument as well.... that could work for the pipe operator as well, of course, that's why I said that the two things are basically equivalent, just with slightly different notation).
[1] https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
Of course it's not built in, which I understand is annoying if that's your preferred coding style. I personally am sad that really good traits aren't encoded in the language.
array |> mean |> round
For scenarios with named arguments there is a little not so cleaner workaround
array |> mean |> x->round(x, digits=2)
is how mathematicians have thought for hundreds of years now. They do so in this way, because it's indeed easier to parse, and philosophically it correctly represents what is happening mathematically.
> array.mean().round(digits=2)
is how a subset of software engineers have thought about computation in the last couple of decades.
Technology should whenever possible adopt already existing conventions, because technology is created to serve the user, and not vice versa.
"Because tradition" is not a good argument. We can and should strive for better.
The modern OOP-derived style of `array.mean().round(digits=2)` is just as exact in its specification, and arguably more semantically precise as well as it organizes the meaningful bits to be together.
Also I think you don't account for how much mathematical notation has changed over the years. The `round(mean(array), digits=2)` structure is relatively recent, early 20th century I believe, and the result of active reforms.
I agree. Math notation has changed in response to problems mathematicians have faced, and as their thinking have evolved. It should not change because tool builders for mathematicians - when the tool builders in most cases are not even professional mathematicians - decide some other notation is better.
> array.mean().round(digits=2)
might be good (it is not) when you are operating on one object. As soon as you have a binary operation (say multiplication), this notation completely breaks down. Because the operation is often symmetric, or "near" symmetric, meaning it is not so different as to write something as ridiculous as x.product(y).
But more importantly, (common/dominant) mathematics is functional in nature. It's decidedly not object oriented. Given a matrix, M, doing something like M.eigenvalues() is vomit-inducing because eigenvalues is not a property of a matrix M. For matrices, its a map from a set of linear transformations (of which M is merely one item) onto a set of numbers. It is its own thing, separate from M.
M = [1 2 3; 4 5 6; 7 8 9]
λ = eigvals(M)
Which is much the same.Personally I think of them as a characteristic of the matrix (but I just write numerical software, I’m not a mathematician) and implement them as the result of a process. So I don’t really see any reason to favor either syntax or call one more mathematical.
I don't think this subthread is going to be productive and I'm bowing out.
Even though the notation changed over the years, the paradigm of "numbers as immutable entities and pure functions" has been the dominant way that math is presented, compared to something like "encapsulated objects that interact with each other via sending messages".
I don't think this has to be this way, and I do think that "real math" can also be laid out assuming principles of OOP. However, I do suspect it's the way it is because the laws of nature are unchanging, in contrast to the logic of a business application.
Because Julia is a tool with the target audience of mathematicians and scientists, I think it's a sensible decision to embrace the usual way of thinking, as opposed to presenting a relatively different way of thinking which steepens the learning curve. Not because data and functions are fundamentally better than OOP, but because it's more pragmatic for the target audience.
The entire field of theoretical computer science, to which functional programming and type theory is closely tied, is a branch of mathematics. The Church-Turing thesis which gives both to our field equates the two at a very fundamental level. Questions about type theory, programming language design, and programmer ergonomics are fundamentally about math and applied math.
Maybe you and the other poster have in mind specific fields of math, but then you need to make claims for why those fields are sufficiently different as to be exempt from applicability of any of the advances in notation observed in other fields.
Your implicit assumption that you can divide computer science into a different bucket from “real math” is incorrect, and gatekeeping.
As I said though, I don’t think this is a profitable debate to have here on HN.
> Maybe you and the other poster have in mind specific fields of math,
Yes, because this is about Julia, I assumed we are talking about the specific fields of math that happen to be commonly used in mathematical and scientific computing, such as the ones learned in university math and science courses.
> Your implicit assumption that you can divide computer science into a different bucket from “real math” is incorrect, and gatekeeping.
It is regretful that we had a miscommunication. I agree with you that computer science belongs into the same bucket as "real math". The thing is, in the context of Julia it is not easy to read "math" as "math, but not only the domains that Julia is concerned in, but really all fields of math, including theoretical computer science". At least thanks to your comment, I see what you mean more clearly, and I think it'll help some other potential readers as well.
> but then you need to make claims for why those fields are sufficiently different as to be exempt from applicability of any of the advances in notation observed in other fields.
I'm curious to know specifically about the specific advances of "notation observed in other fields". By this you mean dot notation for method application? I'm unsure if `a.method(b).anotherMethod(c)` is any different from `a |> method(b) |> anotherMethod(c)` (Edit: or `(anotherMethod(c) . method(b))(a)`) notation-wise.
`x.plus(y)` dispatches based on (runtime) type of x (polymorphism) (single dispatch), and might call different functions based on the (compile time) type of y (function overloading)
In Julia, it would be `plus(x, y)`, and it can dispatch based on the runtime type of both x and y (or determine the correct method to call at compile time already, if the types of x and y can be determined then).
Julia's execution model is quite distinctive, and it does take a thorough understanding of how it plays out to get native-code speeds out of it.
> The modern OOP-derived style of `array.mean().round(digits=2)` is just as exact in its specification, and arguably more semantically precise as well as it organizes the meaningful bits to be together.
I would have agreed with you if I were developing business applications. However, for the domain of math and science, every piece of material that I have seen assumes the paradigm of numbers as immutable entities and functions, not the paradigm of mutable encapsulated objects and interacting messages. To a mathematician or scientist the semantics of a `Vector` (as data) and a `mean` function that takes the `Vector` as input should be more natural than a black-box `Vector` object that can receive a `mean` method.
It may be philosophically correct per some definition of philosophically correct but this is not what I care about.
For example, writing a function that works on scalars, then mapping or broadcasting it over array input is very good for productivity (and is efficient in Julia). On the other hand, designing an inherently array-based algorithm is often a headache that one must contend with for the sake of performance in numpy.
So, if you design somewhat complex or novel algorithms, I don't think object-dot notation, or the lack thereof, has a significant impact on productivity.
Julia is not really a pure functional programming language, though it does support that style if one desires.
It's actually a "multiple dispatch" language... thinking of OOP as single dispatch, Julia is conceptually OOP on steroids -- but to benefit from the full expressiveness, the programming style looks different from the typical OOP syntax/patterns one is used to.
For example: https://diffsharp.github.io/index.html
m = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]But for (entry-level) learning and possibly pivoting to application, Julia is delightful to use and can transit into some symbolics and numerical. Besides, it's free and open source.
They do not.
Unless you're working on undergraduate integrals then it's pretty useless for advanced stuff without writing your own library. By that point you may as well write it in a language that's more performant and cheaper.
Hard disagree. Mathematica's symbolic dexterity makes abstract reasoning (with equations/expressions) very easy. Think of it as the companion tool for anyone doing pages of algebra that would go into a paper, or form the backend for some code.
The numerical capabilities of Mathematica are passable but nothing fancy. Once you have the math figured out, you might even want to reimplement "in a language that's more performant and cheaper." But I haven't see anything come close to Mathematica for convenience of symbolic reasoning -- not just as a technology (lisp is pretty good) but as a ready-for-use product.
But mostly for symbolic algebriac manipulation. I used it during my phd to work with groups. Instead of having to calculate stuff by hand, you can just ask Mathematica to do it. Also lots of stuff with tensors in GR is so easy to do in Mathematica.
The people who would get most out of it are students, but for some god forsaken reason universities don't support them.
I was in a pilot class with Mathematica back in 2006 and the review of the class were _all_ 5 stars and students on average got 10% higher marks in all other subjects they took that year.
They didn't run the course again.
Sagemath is now equally good if a teacher defines a DSL for the students to use in a class.
For the mathematical constructs we care about in symbolic programming, I have found Python's syntax and Sage's menagerie of objects awful to use. Initially you feel comfortable, but when you want to do some real work, it gets horribly in the way. The Wolfram language, a LISP variant, is less familiar and harder for a newbie to learn but it is vastly superior for actual work.
The only "problem" with sagemath is that it is based on Python. The rationale is that Python is easy to start using and widely known. This is the usual "make it easy for newcomers" trap.
For the mathematical constructs we care about in symbolic programming, I have found Python's syntax and Sage's menagerie of objects awful to use. Initially you feel comfortable, but when you want to do some real work, it gets horribly in the way. The Wolfram language, not a LISP variant, is less familiar and harder for a newbie to learn but it is vastly superior for actual work.
> There are many examples of integrating a computer algebra system (such as Mathematica, Maple, or Sage) into the calculus conversation. Computer algebra systems can be magical. The popular WolframAlpha website calls the full power of Mathematica while allowing an informal syntax that is flexible enough to be used as a backend for Apple’s Siri feature. (“Siri what is the graph of x squared minus 4?”) For learning purposes, computer algebra systems model very well the algebraic/symbolic treatment of the material while providing means to illustrate the numeric aspects. These notes are a bit different in that Julia is primarily used for the numeric style of computing and the algebraic/symbolic treatment is added on. Doing the symbolic treatment by hand can be very beneficial while learning, and computer algebra systems make those exercises seem kind of redundant, as the finished product can be produced much easier.
TL;DR: they want the student to actually do some of the work that Mathematic magics away.
The above example in J:
3 3 $ i.9
0 1 23 4 5
6 7 8
3 3 $ >: i.9
1 2 34 5 6
7 8 9
J is 0-indexed. ">:" is the increment operator. APL allows you to set a parameter to do 1-indexed vs. 0-indexed.
APL:
3 3 ρ ι9
1 2 34 5 6
7 8 9
There is a Calculus text for learning it with J:
I honestly thought apl was extinct
* Prefixing a variable with a scaler will implicitly multiply, as in standard math notation (e.g. 3x would evaluate to 6 if x is 2)
* Lots of unicode support. Some feel almost gratuitous (∈ and ∉ are operators that work as you would expect) but it's pretty nice to have π predefined (it's even an irrational datatype) and to use √ as an operator (e.g. √2 is a valid expression and evaluates to a float). It's not just that Julia supports these constructions but provides a convenient way to get them to appear.
* This is a little less relevant for calculus but vectors and matrices are fist class types in Julia. Entering and visually parsing matrices is so much easier in Julia than in Python.
vs. Transposition is a single character operator ('). Dot product can be done with the dot operator (m ⋅ n). A\b works as it does in matlab.* Julia supports broadcasting. It also has comprehensions but with broadcasting I personally find much less need for comprehensions.
* Rationals are built-in with very simple syntax (1//2))