Which concepts known from Python programming are useful in understanding Julia and which are not?

One pattern that gave me some friction when switching was how to handle complicated transformations mapped over a large input. In Python, I might do:

input_list = [i for i in range(100_000)
def foo(x):
    return x/2
def bar(x):
    return x+5
def baz(x):
    return str(x)

output_list = [baz(bar(foo(x))) for x in input_list]

Whereas in Julia, I probably want to use broadcasting

input_list = collect(1:100_000)
output_list = @. baz(bar(foo(input_list)))

My Python brain says “Yikes, that’s 3 iterations when I could do it in 1”, but Julia will do loop fusion for me, so I don’t need to worry about it.

1 Like

well, not sure Python is the one being actively harmful here :sweat:

3 Likes

This might be prettier?

output_list = @. input_list |> foo |> bar |> baz

(I don’t use that often, but it works, doesn’t it?)

3 Likes

I’ve changed my language to be more clearly non-judgmental. The point is that files and modules exist in both languages but behave quite differently in how they’re imported and used. Of course whichever model you prefer will be a preference. The challenge (or “harm”) to learning is simply that they’re different-but-named-the-same.

3 Likes

One difference that can trip up Python users new to Julia is the scoping rules.

In Python, the first x = 2 in a function defines a new x unless explicitly specified nonlocal.

In [4]: def f():
   ...:     x = 1
   ...:     def g():
   ...:         x = 2
   ...:     g()
   ...:     return x
   ...: 

In [5]: f()
Out[5]: 1

In Julia it reuses the one from the enclosing scope:

julia> function f()
           x = 1
           function g()
               x = 2
           end
           g()
           return x
       end
f (generic function with 1 method)

julia> f()
2

In Python there are two different xs; in Julia there is only one.

Personally I think this Julia behavior risks accidentally mutating an existing variable so I’d prefer that separate syntax be used for definition = and reassignment :=, but for now you just have to be careful.

11 Likes

Yeah, absolutely. I love the chain syntax! But I wanted to highlight the differences between broadcasting and list comprehension without adding another syntax :slight_smile:

1 Like

There are for sure concepts that are harmful to the sanity of own thoughts. If I mean 1 to 10 , I mean [1, 2, … , 9, 10] and not [1, 2, … 8, 9]. It is also sure that it is possible to adapt to any pattern keeping clear and sane mind, but some of such patterns are result of author’s confusion, so if you give-in to accept and assimilate them, you assimilate a bit of the confusion along with it. After 30 years of Python I am still forgetting the colon after function or loop definition and feel restricted in design of code text pattern by being forced to use appropriate indentation. Python, which aligned with my mental model much better than anything else 30 years ago does it no more - hence my search for an alternative. I was into Emacs … so learned to write eLisp code … but dropped it because of stubborn stackexchange community and the mess of Emacs core code base. Ruby and Lua are from my current perspective a better choice than Python considering the syntax. Python has much of the batteries included … and LLMs are currently best while writing Python code and fail on other programming languages. What I need a programming language for is to be the one which aligns best with the goal of respecting custom user preferences allowing adaptation to any other way of expressing algorithms and to the own “inner language”. If you like I encourage you to check out what oOo is about … I am as oOosys on github and dev.to - this would help to better understand my motivation and what I am actually after asking questions where the goal is always increasing the level of understanding - anything else is an add-on to it.

2 Likes

This seems to go well along with my inner language and thought model … classes and inheritance were from my perspective always more confusing than helpful. The entire OOP concept resulted overall in the mainstream in code polluted with unnecessary encapsulations often also for a single line of code only … making it harder to grasp what the code is about because of the confusion out of which the classes and their hierarchy were designed in first place.

1 Like

10 posts were split to a new topic: Confusion about scope in try-catch blocks

The performance model in Python (“loops / user code = slow; vectorized / built-in = fast”) can be misleading if you confuse it for a model of how the computer actually works, or if you try to apply it to languages that are more compiler-friendly.

12 Likes

dir… Thank you. I remember now.

1 Like

methodswith is a good start… thank you… :slightly_smiling_face:

But one thing I find confusing with Julia is how verbose it is in its answers… If I type methodswith(DataFrame) , I have pages of information in the REPL. It would be nice if it was more concise or if it is allowed me to hit the space bar to go to the next page…

Edit: apropos is much more concise…

1 Like

TerminalPager.jl enables spacebar to move pages.

using DataFrames, TerminalPager
methodswith(DataFrame) |> pager
4 Likes

Many highly respected educators focus on mental models. Here’s one great example:

The question is not if our students will create mental models or not – we all form mental models as we learn. It’s our job as teachers to be thoughtful and help our students form useful and accurate ones.

2 Likes

Yes … some teachers are possibly aware of it … but are there experts extremely well skilled in programming aware of it too? May experience is that programmer have hard time to be able to grasp how it can be that others don’t share their inner mental model they are not aware of, but wondering about the effects of unable to arrive at some insight where issues others have could origin from …

A thought about the default numbering of an array/matrix diagonals:

Chosing the main diagonal as special with the number ZERO appears to me as insane as starting counting items in a container labeling the first index as 0 . If you want all diagonals … one after another … you can’t iterate over collect([1:sum(arr.size())] … you need first to determine if the matrix width is larger than height and then cover both cases differently … this is insane … there are width+height-1 diagonals . From my perspective the only sane approach to iterate over all of them is to start with the first one and go up to width+hight-1 … instead of being forced to cover two cases with separate code. Even if you want get only the upper or the lower ones … you still need to distinguish the cases depending on width < height to get the range of diagonal k-values right …

Guess it depends on what you want to do with diagonals. In linear algebra, iterating over all of them is quite rare. Instead, working with the main diagonal or the ones just below or just above is quite natural. In that case, numbering them is as 0, negative (below) and positive (above) is good and in particular, the desired diagonal can be identified by a fixed offset independent of the size of the matrix.

1 Like

Sure the purpose and convenience can lead to making the main diagonal the center of the array/matrix world considering it from the “Earth in the Center of the Universe” point of view. If you are not interested in understanding why the planets make strange moves in the sky this is sure OK. A general purpose method may support an additional option for the convenience purposes making the main (anti-)diagonal the reference point on demand, but forcing everyone to follow the convenience of the majority is putting constrains on the freedom of choice. In my eyes a programming language should support the inner mental model of every possible user and refrain from trying to impose an own one major use based-one needed to be learned and assimilated first before using. If you use a fix offset no matter the array/matrix size you will run into trouble with indexing out of the available range. Just transpose an array and the fixed distance from the main diagonal will cause an error in case of width much greater than hight of an array/matrix, right?

IMO, there are 4 reasons why numbering diagonals from corner to corner is crazier:

  1. it is never(?) necessary. I don’t think I’ve ever heard of a case where “the third diagonal from the top right corner” has ever been needed for anything in an isolated context. Diagonals only ever matter if they’re relative to the diagonal. This is in direct contradiction to the “earth is the center of the universe” argument you’re making. The main diagonal is the center of the universe when talking about diagonals… Any other paradigm is prone to making “the planets make strange moves in the sky”.
  2. It’s the diagonal that includes/starts with the first element. Surely it’s crazier to arbitrarily say the diagonal that includes the top-right element should be the “first one”? You could argue that the main diagonal should have index 1 instead of 0 though, and I could agree with you there tbh. The counterargument is that it makes the upper and lower diagonals inconsistent.
  3. Iterating [diag(A, i) for i in 1:size(A,1)+size(A,2)-1] (the 1:k proposal) is no easier or clearer than [diag(A, i) for i in -(size(A, 1)-1):size(A, 2)-1] (the current approach). Again, not that this code is frequently written… but if it has been, that’s what it would look like.
  4. It even makes sense from the perspective of diagm(2 => [1, 2, 3]). Any other scheme of referring to diagonals doesn’t give enough information to actually build the matrix.
2 Likes

This does not generally work because you need to replace size(A, 1) with size(A, 1) > size(A, 2) ? size(A, 1) : size(A, 2) and the other one reference to size(A, 2) accordingly too.

The argument “code is not frequently written” is in my eyes not a valid argument at all, because the entire mainstream can go wrong … so what is today “frequently” can be tomorrow “obsolete” or what is considered “useful” from one perspective can be considered “insane” from another. It is about sanity of mind … and the “Earth in the center of the Universe” is sure a valid approach, but … generally just insane view on the actual reality leading to issues in another contexts than local ones. So argumentation that almost everyone does not leave Earth to the orbit or to the Moon/Planets does not make the Earth centered view on the Universe a sane one.