Hacker News new | past | comments | ask | show | jobs | submit login
Python should take a lesson from APL: Walrus operator not needed
102 points by robomartin 34 days ago | hide | past | web | favorite | 106 comments
During a recent HN discussion about the walrus operator I came to realize yet another advantage of notation. I used APL professionally for about ten years, which made it an obvious source of inspiration for an example that, in my opinion, demonstrates why the Python team missed a very valuable opportunity to take this wonderful language and start exploring the judicious introduction of notation as a valuable tool for thought (borrowing from Ken Iverson's APL paper with that title [0]).

To simplify, I'll define the desire for this walrus operator ":=" as "wanting to be able to make assignments within syntax where it was previously impossible":

    if x = 5    # was impossible

    # and now

    if x := 5  # makes is possible
A more elaborate example given in the PEP goes like this:

    Current:

    reductor = dispatch_table.get(cls)
    if reductor:
        rv = reductor(x)
    else:
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor:
            rv = reductor(4)
        else:
            reductor = getattr(x, "__reduce__", None)
            if reductor:
                rv = reductor()
            else:
                raise Error(
                    "un(deep)copyable object of type %s" % cls)

    Improved:


    if reductor := dispatch_table.get(cls):
        rv = reductor(x)
    elif reductor := getattr(x, "__reduce_ex__", None):
        rv = reductor(4)
    elif reductor := getattr(x, "__reduce__", None):
        rv = reductor()
    else:
        raise Error("un(deep)copyable object of type %s" % cls)
 
At first I thought, well, just extend "=" and be done with it. The HN thread resulted in many comments against this idea. The one that made me think was this one [1]:

    These two are syntactically equal and in Python there's no 
    way a linter can distinguish between these two:

        if reductor = dispatch_table.get(cls):
        if reductor == dispatch_table.get(cls):
    
    A human being can only distinguish them through careful inspection. 
    The walrus operator not only prevents that problem, but makes 
    the intent unambiguous.
Which is a perfectly valid point. I get it.

Still, the idea of two assignment operators just didn't sit well with me. That's when I realized I had seen this kind of a problem nearly thirty years ago, with the introduction of "J". I won't get into the details unless someone is interested, I'll just say that J turned APL into ASCII soup. It was and is ugly and it completely misses the point of the very reason APL has specialized notation; the very thing Iverson highlighted in his paper [0].

Back to Python.

This entire mess could have been avoided by making one simple change that would have possibly nudged the language towards a very interesting era, one where a specialized programming notation could be evolved over time for the benefit of all. That simple change would have been the introduction and adoption of APL's own assignment operator: "←"

In other words, these two things would have been equivalent in Python:

    a ← 23

    a = 23
What's neat about this is that both human and automated tools (linters, etc.) would have no problem understanding the difference between these:

    if reductor ← dispatch_table.get(cls):
    if reductor == dispatch_table.get(cls):
And the larger example would become this:

    if reductor ← dispatch_table.get(cls):
        rv ← reductor(x)
    elif reductor ← getattr(x, "__reduce_ex__", None):
        rv ← reductor(4)
    elif reductor ← getattr(x, "__reduce__", None):
        rv ← reductor()
    else:
        raise Error("un(deep)copyable object of type %s" % cls)
This assignment operator would work everywhere and, for a period of time, the "=" operator would be retained. The good news is that old code could be updated with a simple search-and-replace. In fact, code editors could even display "=" as "←" as an option. The transition to only allowing "←" (and perhaps other symbols) could be planned for Python 4.

Clean, simple and forward-looking. That, to me, is a good solution. Today we have "=" and ":=" which, from my opinionated perspective, does not represent progress at all.

[0] http://www.eecg.toronto.edu/~jzhu/csc326/readings/iverson.pd...

[1] https://news.ycombinator.com/item?id=21426338




I fell like this is a troll so good I'm even questioning if it's really a troll. But in short: you can't require non-ascii symbols for languages which are supposed to be accessible. And <- won't work since it's a valid syntax in other contexts.


I heard the same complaints about indentation for scope, 20 or 25 years ago, and yet it seems to have worked out OK for them. It's interesting how many of these rules we know to be true, aren't.

I for one am tired of programming languages trying to eke out the best ASCII art they can manage with the symbols that happened to end up on American keyboards by historical accident. Or wanting to look 97% the same as ANSI C, even though their semantics are completely different.

For the people who really cannot type anything but US-ASCII on their DEC VT100 terminals, and don't use an editor or operating system which allows them to compose other characters, there are still ASCII combinations that look more or less like a left-arrow, and which are invalid Python syntax today.


It was successful trolling. Look at all the comments...


APL generally feels like a troll, like INTERCAL, but from my interactions with APL advocates I think they are sincere.


Yeah, I wouldn't even think it's weird in APL areas. But when someone says "let's take one of the most beginner-accessible languages and add a required symbol which is not on the keyboard", that's... something else.


I really liked APL when I learned it.

I actually think it sort of aligned with the eccentric academics that used it - folks that published research papers with figures requiring the greek alphabet and math symbols.

But I can see how people would think it was a troll.

I recall it doubled-down on the crazy character set and required overstriking multiple characters to create some operators (for example box + divide to get a domino like character)

By comparison walrus is fine, and it is even friendlier with the disarming animal-themed name.


Like ⌹? I wonder what it means. There's all these APL symbols in Unicode and I don't understand what they mean. But they never look serious enough to make me want to learn the language. I guess the modern APL character soup is Haskell where people can propose (but never implement) something like ⟦ xx ∋ yy ⟧ which looks vastly more serious and any stranger can immediately grasp its meaning, a trait shared with every ascii alphabet soup programming language in the world.


If you look at the symbol, it looks like a rectangle with a division symbol inside. A rectangle is reminiscent of the shape of a matrix. This is what it does:

Monadic form: Matrix inverse.

      a ← 3 3 ⍴ ⍳9
      a
    1 2 3
    4 5 6
    7 8 9

      ⌹a
    ¯4.849821517E14  9.699643034E14 ¯4.849821517E14
     9.699643034E14 ¯1.939928607E15  9.699643034E14
    ¯4.849821517E14  9.699643034E14 ¯4.849821517E14
Dyadic form: Matrix division.

      b ← 1 2 3

      b ⌹ a
    0.7357839757 ¯1.471567951 1.069117309 
To put this into context, say you have a set of linear equations with three unknowns, x, y and z:

    -8 = 3x + 2y - z
    19 = x - y + 3z
     0 = 5x + 2y
We want to solve for x, y and z.

Matrix division is the answer.

First we grab the coefficients and stuff them into a matrix:

    c ← 3 3 ⍴ 3 2 ¯1 1 ¯1 3 5 2 0

      c
    3  2 ¯1
    1 ¯1  3
    5  2  0
Next we assign the constants to a vector (which can also be considered a 1x3 matrix):

      k ← ¯8 19 0

      k
    ¯8 19 0 
Note that APL uses the "high minus" to denote negative values in order to avoid confusion with negation "-". For example:

    -1 2 3
yields:

    ¯1 ¯2 ¯3 
If you only wanted a negative 1 the correct syntax would be:

    ¯1 2 3
Anyhow, we now have:

      k
    ¯8 19 0 

      c
    3  2 ¯1
    1 ¯1  3
    5  2  0
In order to solve the system of three simultaneous equations with three unknowns we just need to use matrix division:

      k ⌹ c
    2 ¯5 4 
The answer is a vector where the elements correspond to x, y and z, respectively.

This will work so long as the matrix has an inverse and will work for a system of n equations with n unknowns.

Incidentally, if we want to keep all three equations, constants and coefficients in a single matrix we can do that just as well:

      d ← 3 4 ⍴ ¯8 3 2 ¯1 19 1 ¯1 3 0 5 2 0 
      d
    ¯8 3  2 ¯1
    19 1 ¯1  3
     0 5  2  0
We can then use the same matrix division expression to solve the system of equations by slicing-up the matrix to get our constants and coefficients:

This "take"s (↑) the first column from the matrix (along the second dimension, hence the [2])...

      1↑[2]d
    ¯8
    19
     0
and this takes the last three columns:

      ¯3↑[2]d
    3  2 ¯1
    1 ¯1  3
    5  2  0
You can also "drop" (↓) the first column, which is a better idea because this makes the code independent of the size of the matrix:

      1↓[2]d
    3  2 ¯1
    1 ¯1  3
    5  2  0
Plugging into the prior expression of this form:

      k ⌹ c
we would have:

      (1↑[2]d) ⌹ 1↓[2]d
     2
    ¯5
     4
This gives us a 3x1 matrix as the result. If we want a vector all we need to do is add the "ravel" operator "," (the monadic form of concatenation) on the left:

      ,(1↑[2]d) ⌹ ¯3↑[2]d
    2 ¯5 4 
Or, we could, if it made sense "laminate" the column vector result on the back side of the matrix so that we have constants, coefficients and the solution vector all in a single variable or data structure. Like this:

      d ← d,(1↑[2]d) ⌹ ¯3↑[2]d
      d
    ¯8 3  2 ¯1  2
    19 1 ¯1  3 ¯5
     0 5  2  0  4
That gives us everything in a nice tight package that we can use for other work.

Extracting the various elements from this matrix is easy:

Constants:

      1↑[2]d
    ¯8
    19
     0
Results:

      ¯1↑[2]d
     2
    ¯5
     4
Coefficients:

      ¯1↓[2]1↓[2]d
    3  2 ¯1
    1 ¯1  3
    5  2  0
or, using indexing instead of sequential "drop" (the disadvantage being that the "drop" approach is independent of matrix size):

      d[;2 3 4]
    3  2 ¯1
    1 ¯1  3
    5  2  0

If these concepts are foreign to you, google "solving simultaneous equations with linear algebra". It's neat stuff.


You have posted an amazing and comprehensive comment.

> If you look at the symbol, it looks like a rectangle with a division symbol inside.

If you skip exactly 20 minutes into this video it IS EXACTLY a rectangle with a division symbol inside:

https://www.youtube.com/watch?v=_DTpQ4Kk2wA

I don't know whether to think "thank goodness for unicode" or "unicode is an enabler of problematic practices"

:)


I got started in the early '80's, 1982 if I remember correctly. I actually used a teletype for a (thankfully) very short period of time --just like what the presenter is using in this video. In the most primitive early days you had to type two characters, the rectangle and then go back and type the division symbol. Again, thankfully, that lasted a very short time for me.

What most people do not realize when they discuss APL today is that back in 1962 and going into the mid to late '80's APL was an incredibly advanced tool when compared to almost anything available at the time. I mean, people were programming in Fortran, Cobol, C, Forth, Lisp, Basic and a few other languages. They were incredibly primitive when compared to APL.

I was doing crazy multi-dimensional array work in physics that would have required massive amounts of C code to implement, not to mention that I could type a few lines of code that represented weeks of development in other languages.

It is easy to be critical of APL today and not realize that its creation back in the '60's and usage for the next 20 to 30 years was sheer genius.

Today most of these capabilities are not found as inherent parts of languages but as an array of domain specific libraries. The tool-set available to an APL programmer back around the time of this video and for the next decade (at least) was without comparison in the world of computing.

I am very glad to have been shoved into an APL class in college. It made a huge difference in my life and taught me to think about computing in a very different manner.


Go managed to use <- still..


Being "valid syntax in other contexts" is language dependent.

Go works by taking <- as an operator at a higher priority than the alternative parse, if indeed the "alternative parse" is even possible in the grammar (I'm not checking for a HN post); if you start with https://play.golang.org/p/S_XOiKdyj1A and remove the space after the less-than symbol and hit run, you'll see it no longer compiles properly. So <- only has one interpretation in Go. By contrast:

    $ python
    Python 2.7.15+ ...
    >>> x = -3
    >>> if x<-2:
    ...     print("Hello world")
    ... 
    Hello world
<- appears in a perfectly valid context now that Python parses, in what happens to be the very place that is under discussion, so changing x<-2 to be "set x to 2" would be a non-backwards compatible change in Python.


For a language that's infamous for its strictness in regards to whitespace, I'm surprised "x<-2" is allowed.

Why not just require whitespace around every operator?


It's not strictness with regards to whitespace, it's just using indentation to delimit blocks. The rest is less strict than C, not requiring spaces between numbers and keywords:

  print(1if 2else 3)
is valid Pythonese.


Python isn't that strict about whitespace? It's just using indents to delineate blocks and newlines implicitly terminate statements (unless you have an unclosed brace).

Anywhere else, you can add as much whitespace as you want.


But in short: you can't require non-ascii symbols for languages which are supposed to be accessible

While I agree that everything should be ASCII the Python community believes everything should be Unicode, since Python 3.


1. ASCII is a subset of Unicode. You can be both.

2. The change in Python 3 is the introduction of a distinct string type specifically because there are things that are not Unicode, namely bytestrings. Several things that could have been implicitly Unicode in Python 2 have become not-Unicode in Python 3 (e.g., the return value of subprocess.check_output).

3. You enter bytestring literals in ASCII.


Can you enter BEL, NUL, line feed, and " in a bytestring literal, and if so, can a person seriously say with a straight face that 0x5c 0x6e is mathematically and physically identical with 0x0a. I suppose I could believe in ascii bytestring literals if the string literal were length tagged pascal strings.


> 1. ASCII is a subset of Unicode. You can be both.

You can encode a character, just like you can express Farsi symbols and smiley faces and other emojis, but no keyboard layout supports it. What's the point of supporting a character if no standard input device lets you type it?


Python 3 also allows for non-ASCII identifiers.


Just to be clear, this comment was just a clarification on what goatinaboat might have been getting at, I was in no way suggesting that the snappy claim

> While I agree that everything should be ASCII the Python community believes everything should be Unicode, since Python 3.

had any substance.

It's just important to know IMHO that not only string handling changed in Python 3, but that you can indeed Unicode-ify your identifiers now as well to a certain extend (see https://www.python.org/dev/peps/pep-3131/). I still don't do it for interoperability reasons.


Indeed it’s right here https://www.python.org/dev/peps/pep-3131/

Seems a few people on this thread don’t know Python as well as they think they do


Plenty of mainstream languages allow non-ASCII characters in identifiers. Pretty much none of them (at least not one that I've worked with) have non-ASCII delimiters/operators/keywords/reserved words. Knowing non-ASCII characters is supported in identifiers has nothing to do with objecting to non-ASCII symbols in the language syntax.

(Btw plenty of languages tell you non-ASCII characters are supported in identifier names in the first pages of official tutorials, e.g. golang and Swift off the top of my head, so knowing this kind of stuff doesn't make you a language expert.)


knowing this kind of stuff doesn't make you a language expert

Never said it did. But I will say for a fact that those who don’t know it are not language experts.


Nobody in this thread disputed that Python 3 allows for non-ASCII identifiers.


Encoding of keywords and the default string abstract representation are entirely different things.


There is a difference between using unicode for strings, which necessarily must support internationalization, and the syntax of the language itself


As someone who's had to teach programming to novices, I woefully underestimated how confusing (and justifiably so) `x = 1` notation is to students who didn't have the benefit of a CS education (i.e. exposure to C/C++/Java, where you eventually take the syntax for granted). I almost considered switching to teaching R, which has the explicit assignment operator `<-` (in addition to the traditional `=`).

I wouldn't mind seeing `<-` in Python. My biggest issue with `←` is that it's too inconvenient to type, as others have pointed out.


But programmers normally don't type things out long hand. They use tools for their language which automatically add indents and autocomplete symbols. If the only alternative parse for <- in Python is "less than minus", it should be fairly straightforward to program any serious IDE to replace <- with ← in the right context.

Would programming in a language with first class variables like Haskell help you? In Haskell, x = y means "let x and y be synonyms, which the compiler can freely substitute" whereas to mutate a value you have to say something like `put x y` (x is not a variable, it merely refers to a variable). It's approach is so different from standard that it explodes my head but I wonder if it's more approachable to nubs.


Unless I misunderstand what you are proposing, if you are going to deprecate both = and := in favor of ←, why not simply deprecate = in favor of := ?


This was my first thought as well. Of course, getting rid of `=` would make the conundrum about moving Python from version 2 to version 3 seem like a quiet evening at home, but there is no practical reason why `:=` wouldn't work in all contexts as `=` (currently, a simple assignment is a syntax error).


Actually I reckon it would be an easy technical change - it’s almost a find and replace (I think)! Emotionally, I don’t think people, in general, would handle it well though. The Python 2 to 3 was mostly because it was not possible to automatically convert from the former to the latter


APL's assignment operator is very strange to me; simple array of numbers assigned to 'nums':

    nums←1 2 3 4 5
Then catenate (append) a 6 to the right side end of it:

          nums,←6
          nums
    1 2 3 4 5 6 
Now use position-reversed catenate to append a 6 to the left side end of it; how does that work?:

          nums,⍨←6
          nums
    6 1 2 3 4 5 6 
Now use addition-assign to add 2 to each item; how:

          nums+←2
          nums
    8 3 4 5 6 7 8 
Now assign a single value into non-consecutive locations using array subscript notation on the left side of the assignment; that's unusual:

          nums[2 4]←99
          nums
    8 99 4 99 6 7 8 
Now do a max-assignment to replace values which are less than 7 with 7:

          nums⌈←7
          nums
    8 99 7 99 7 7 8 
      
Nums reset to 1 2 3 4 5, laminated with 'hello' to pair each number with a letter:

          nums←1 2 3 4 5
          nums,[0.5]'hello'
    1 2 3 4 5
    h e l l o
          nums
    1 2 3 4 5 
Want to do that and update nums? Stick an assignment in there:

          nums←1 2 3 4 5
          nums,[0.5]←'hello'
          nums
    1 2 3 4 5
    h e l l o
It seems so odd that this can pull values out and modify them with the value being assigned and put the result back, assigning to 'nums' and modifying it, while keeping the shape and structure of it.


For my amusement mostly, here's the Julia translation:

    nums = [1,2,3,4,5]
    nums ↑ 6              #  const ↑ = push!; const ⤉ = pushfirst!
    nums ⤉ 6  
    nums .+= 2            # addition-assign to add 2 to each item
    nums[[2,4]] .= 99     
    nums
    nums ⩠ 7              #  ⩠(a,b) = a[a.<b].=b
    nums

    nums = [1,2,3,4,5]
    nums ↔ "hello"        #  ↔(a,b) = hcat(collect(a), collect(b))
    nums = nums ↔ "hello" # re-binding nums


= is not an operator. It's part of the assignment statement[1] syntax.

Here's the full list of Python operators[2] as of 3.8:

    +       -       *       **      /       //      %      @
    <<      >>      &       |       ^       ~       :=
    <       >       <=      >=      ==      !=
The post is probably just trolling, but still.

[1] https://docs.python.org/3/reference/simple_stmts.html#gramma...

[2] https://docs.python.org/3/reference/lexical_analysis.html#op...


In summary, Python needs it to be written := and not ← because the latter is not ASCII and not on standard keyboard layouts. And it cannot be written <- either because that would redefine what "if x<-5" currently means, ie. "if x < -5".

Interestingly, the PEP considered the close alternatives "if x from -5" and "if -5 -> x": https://www.python.org/dev/peps/pep-0572/#alternative-spelli...


That's not on my keyboard. Assuming you are serious, if you think a mainstream language like Python would ever accept an operator that can't be typed then you're mistaken.


I wanted to just drop a comment that I appreciate the time you took into this write up with code examples. It made understanding your POV very easy.


Thank you. I appreciate your comment very much.


I like the trade-off C/C++ compilers (with a reasonable amount of warnings set) made... warn about constructs like "if (a=b)" unless the user adds an extra pair of parentheses, like "if ((a=b))".


It gets even better. := is an abandoned assignment operator in R (derived from ←). It is still in the syntax with no default implementation. A few packages use it (data.table, dplyr, wrapr) for quoted assignment tasks.


Python simply should never had = as assignment, and always used := just like pascal does. Using = for assignment and == is part of C's toxic legacy.


I like it! Reminds me of one of my favorite Haskell extensions: https://wiki.haskell.org/Unicode-symbols

I mean, I've never attempted to actually type that beastly notation during programming, but it does print out quite beautifully!


It's not beastly. Just a few :imap's will get you there in vim.


Smalltalk (among other languages of the 1960s and 1970s) had a left arrow symbol as the assignment operator. 95 was ← instead of _ in ASCII pre-1967.

Also, anyone pretending to be a competent computer programmer while whining about being unable to use Unicode in your text editor, please find a new hobby.


> Also, anyone pretending to be a competent computer programmer while whining about being unable to use Unicode in your text editor, please find a new hobby.

Yes. It seems like programmers are people who make the technology of the next generation using the technology of the previous generation.


I think historically the "←" was preferred but many keyboards (card punch machines) did not have the "←" so the used the ":=" as a simplified (more commonly accessible) version of "←"


What to say, imagine my surprise seeing this on the first page of HN last night. Got my 15 minutes, I guess.

As is often the case, the responses contain a bunch of hateful stuff as well as people who actually try to engage with the ideas presented in a constructive manner. To state the obvious: I am not changing anything, so chill people.

Now to address some of the responses, starting with the best:

> I wanted to just drop a comment that I appreciate the time you took into this write up with code examples. It made understanding your POV very easy.

Thank you. Some of the comments lead me to believe we might have created a society where everything needs to be done in 140 characters. I am old school, arguments needs to be presented with enough background information for the reader to understand the perspective that led to the proposal or conclusion. Without that we are all just screaming things and waiting to see what sticks.

> APL is an almost perfect textbook example of write-only code.

Sure, just like math and musical notation are write-only. The only people who say stuff like this are those who don't know a thing about APL. There are two major APL compilers still on the market, the licenses for these cost in excess of $2,500 per year. If APL was "write-only" these two companies (IBM and Dyalog) would have shelved these products decades ago. The fact is that, while not mainstream (and for good reasons), APL is still in use and large codebases are being maintained and expanded. Historically this has mostly been in the financial sector.

Point of interest: My last large APL application was for a hardware/software system used during the race to decode the human genome. Among other things, my application interfaced with the hardware and was used to search for genomes in the sequences being produced. It also produced reports, graphs, managed the database, etc. The hardware portion was coded in Forth.

> APL's assignment operator is very strange to me

Thanks for a very detailed comment with code examples. I think I can help clarify the basic ideas.

The first thing to do is realize the assignment operator isn't passive but rather an active component of a statement being evaluated from right to left. That last part is important, APL evaluates from right to left, the only precedence rule being parenthesis are evaluated first. One could say indexing is also an exception but indexing "variable[23]" is a single monolithic expression that isn't separable, so it is evaluated as one item, not two.

So, if A and B are defined as vectors (APL's name for one dimensional arrays), each consisting of a few random values between 1 and 10:

    a ← 5?10  
    b ← 5?10
    a
    1 10 3 4 9 
    b
    9 5 6 1 4 
We can, for example, concatenate them like this:

    a,b
    1 10 3 4 9 9 5 6 1 4
Again, right-to-left evaluation says "take b, set it up to concatenate to something, ah, we are concatenating to a, another vector".

However, we can modify this process by giving the interpreter further instructions within the assignment. The simplest one is where we select, or index, the vectors and only grab specific elements to concatenate. I'll define a pair of new vectors to make this easier to see:

    c ← "ABCDE"
    d ← "abcde"
    c
    ABCDE
    d
    abcde
With this:

    c,d[1]
    ABCDEa

    c,d[2]
    ABCDEb

    c,d[2 5]
    ABCDEbe

    c[2 5],d
    BEabcde

    c[1],d 
    Aabcde

    c[2],d
    Babcde
The first few are the obvious cases, where we pull elements out of the right side vector and concatenate them to the left side vector. In the other cases we take the entire right side vector and concatenated to a subset of elements of the left side vector. I'd say this forms the basis for understanding the expressions you presented.

The above statements can be combined with assignment in order to do the work more efficiently, plain concatenation being the simplest case:

    e ← "mnlop"
    f ← "MNLOP"
    e
    mnlop
    f
    MNLOP

    e,←f
    e
    mnlopMNLOP
Again, right-to-left, we have vector "f", it will be concatenated to something, the assignment is a concatenation, it is concatenated and assigned to vector "e".

In other words, one can specify operations as part of the assignment. Because evaluation is from right to left, it stands to reason that the add-on operation has to be specified after you told the interpreter you are going to assign. This might be strange from the context of other paradigms but it makes complete sense once you grok the simple "everything is right-to-left" idea. If I were to propose how to read something like ",←" I would say "assign with concatenation"; "+←" would be "assign with addition".

We can do even more. It's easier to show this with multidimensional arrays:

    g ← 3 4 ⍴ "ABCDEFGHIJKL"
    h ← 2 4 ⍴ "abcdefgh"
    i ← 3 3 ⍴ "123456789"

      g
    ABCD
    EFGH
    IJKL

      h
    abcd
    efgh

      i
    123
    456
    789
The first statement reads something like this: Take the character vector "ABCDEFGHIJKL" we are going to reshape it "⍴" using a two dimensional numeric vector "3 4", thereby creating a 3x4 matrix and assign it to "g".

Note that "i" contains characters, not numbers, so if I try to add 1 to every value I get an error:

    i+1   
   DOMAIN ERROR
Now I can do a few things, for example:

Concatenate g and h along the first axis (they both have four columns):

      g,[1]h
    ABCD
    EFGH
    IJKL
    abcd
    efgh
Of course, all I have to do is add the assignment operator to change g:

      g,[1]←h

      g
    ABCD
    EFGH
    IJKL
    abcd
    efgh
As you can see, this is a natural extension of the syntax and the order of evaluation.

We can concatenate along the second axis if we use g and i:

      g,[2]i
    ABCD123
    EFGH456
    IJKL789
Assignment works just as well in this case:

      g,[2]←i

      g
    ABCD123
    EFGH456
    IJKL789
This syntax does have limitations. For example, what if I actually wanted to end-up with this?

    AEIae
    BFJbf
    CGKcg
    DHLdh
Well, we transpose the matrices during the concatenation, this works:

      (⍉g),[2]⍉h
    AEIae
    BFJbf
    CGKcg
    DHLdh
This does not work:

      (⍉g),[2]⍉←h
    SYNTAX ERROR
I don't recall why this is so. I think it has to do with starting to gobble-up piles of memory to keep intermediate results or something like that. You also end-up executing transpose (⍉) twice, which is inefficient. I don't remember.

This is a case where you have to resort to the simpler case:

      ⍉g,[1]h
    AEIae
    BFJbf
    CGKcg
    DHLdh
With assignment:

      g ← ⍉g,[1]h
      g
    AEIae
    BFJbf
    CGKcg
    DHLdh
In other words, concatenate along the first axis, transpose and then assign.

I got a little off track here with respect to the idea of using a different assignment symbol for Python but I thought your comment and implied question was one of the most interesting ones in the thread, which made it fun to address. I hope this helps. Here's a very useful resource:

https://www.dyalog.com/uploads/documents/MasteringDyalogAPL....

Ah, I do have to address one more:

> That's not on my keyboard.

Really people? It's 2019. I was typing APL characters back in the 1980's on DEC and Tektronix terminals and the IBM PC. Imagine the audacity of proposing that one can actually enter things other than ASCII or what's on the keyboard in 2019.

On the free NARS2000 interpreter you enter "←" very simply with "Alt [". In fact most APL characters are entered this way. The reshape operator, rho, "⍴", is entered using "Alt r". Not that complicated. I am sure this is well within the brain capacity of most mortals, it was so over 30 years ago.

Final thought: One of the interesting things about using APL long enough to internalize it (something like what happens when you don't think about reading musical notation any more and just see the music) is that you start thinking and seeing objects and their manipulation in your brain.

The analogy that comes to mind is what I do when I run SolidWorks. I can extrude, cut, scale, rotate, cut holes, create solids from rotated profiles, slice, animate and manipulate complex assemblies of three dimensional objects visually and with ease. The --imperfect-- analogy to conventional programming would be something like AutoCAD, which I have been using since version 1.0 in the dark ages. It's a flat 2D world. Yes, it can do 3D but the cognitive load and lack of tools makes it a terrible tool for working with three dimensional solid objects and assemblies.

Live long and prosper.


R have <- operator and a quick google search Python currently isn't using <-.

So being pragmatic I would choose <- over ←.


Not going to work:

    >>> x = -3
    >>> if x <- 2:
    ...     print 'yes'
    ... 
    yes
    >>> x = 5
    >>> if x <- 2:
    ...     print 'yes'
    ... 
    >>>
Yes, those are ugly examples of bad use of whitespace, but making '<-' mean assignment would be a backwards-incompatible change.

(In case it isn't clear, that evaluates as "if x is less than negative two".)


Didn't C have syntax holes like this?

like c=+1 --> c = +1 or c =+ 1


Originally, in version 0. It was quickly changed to c += 1 to fix that.


I’m annoyed you buried such a simple suggestion after so many examples and pointless tangents. The reason this isn’t done should be obvious to you: your proposed operator isn’t ASCII, unlike everything else in the syntax. (I have to call it “your proposed operator” because I don’t know or care how to type it on my iPhone — so it’s already causing problems.) The nearest ASCII rendering, `<-` as used in R, is syntactically ambiguous. You should have started this post with your actual proposal, instead of prefacing it with pompous language and details.


Also from what I can tell their argument is actually about replacing all uses of `=` with something else, and there's no reason the something else couldn't be `:=` (which has plenty of prior art, even if it has fallen out of favour recently).


> there's no reason the something else couldn't be `:=` (which has plenty of prior art, even if it's fallen out of favour recently).

Pascal was my first language, so when I first learned C (my second language) I actually found the =, == pair much less intuitive than the :=, = pair.


FYI, it's called Bottom Line Up First (BLUF), and comes from military.


https://en.wikipedia.org/wiki/Inverted_pyramid_(journalism) probably predates the US military usage.


Oh many, so many times in my life I wished I could explain my gripe using such a simple term.


[flagged]


You seem awfully quick to declare the poster ignorant.

Under Linux it's entirely trivial to make a different glyph appear on the screen when you press a particular button or combination of buttons. I'm guessing that it's not much harder on other OS. Mac and windows probably have an app with an actual gui even.

Isn't it kind of odd to propose that someone can learn a new programming language but can't learn how to press a different button?


> Under Linux it's entirely trivial to make a different glyph appear on the screen when you press a particular button or combination of buttons.

I'm assuming you're yanking everyone's chain, because suggesting that having to customize standard keyboard layouts just to be able to write code in a programming language is simply absurd.


One can in place run xmodmap to make a single key change or run it on a list of changes to be made saved in a file.


That's about X, not "Linux." How do you do that on a Chromebook? How do you do that on a text-mode console? How do you do that on Wayland? How do you do that over SSH from ConnectBot on Android?


Well for starters we are talking about developers not grandma on her chromebook.

I guess if you are a developer and your choice of tool to develop software is a Chromebook, android device, or text console then step one would be to examine that questionable choice.

If you are using Wayland I would suggest you figure out if wayland adds value to your workflow. If you use an environment that depends on wayland you would just have to accept that its less mature and designed at heart by people who neither value user configuration nor power users.

As such its a bit harder but certainly not impossible and most likely involves creating a custom keyboard layout. It seems likely that someone will eventually figure out how to do xmodmap for wayland but in the meanwhile perhaps the 15 minuted required will be less onerous than the effort required to learn a new language.


> I guess if you are a developer and your choice of tool to develop software is a Chromebook, android device, or text console then step one would be to examine that questionable choice.

Wow! This is what I do, personally. So why don't you start by questioning the choice, if it's so "questionable"? What do you have against it?

And are you claiming you're a better developer than I am? Seems quite unlikely.


You could be and probably are a better developer than I an amateur while opting to use a worse tool.


You haven't defended your claim that any of these tools are worse. I use them because they're better tools.


Please explain why you would want a pixelbook instead of say a Lenovo P71 and why you would in any universe want to code on something running android.


It's odd to propose that everyone is programming in a UTF-8 environment, or that all programmers even know what UTF-8 is. "Should know", sure, but they don't, and trying to make an "is" from an "ought" is a nasty, nasty habit.


The default encoding for Python source code is UTF-8. If they're not using UTF-8 here, it's by choice.


Let me be clear, I don't think the poster is ignorant. I think they're a troll. I think they're entirely aware of how terrible a suggestion it is and getting malicious pleasure from watching people take it seriously.


ComposeKey + < + -


I know many Python programmers, of which exactly one has a compose key.


Python and APL are almost opposite extremes of the readability spectrum. APL is an almost perfect textbook example of write-only code.

I would be extremely weary about borrowing syntax from APL or J.


Great, but I have no way to type that on my keyboard, is it <- ?


:= wasn't needed. = is just fine.


Usually you don’t want to assign things in IF statements. Most of the time “if (a = b)” is intended to be “if (a == b)”, hence the need for the walrus operator.


Ate there any good reasons to assign things in an if statement?


Don't think of the walrus as being an assignment. You can conceptualize it that way, but you shouldn't; that thought will be unhelpful in producing and reading code. Assignment is still the normal =, with a variable on the left and the value you want to put in it on the right.

The walrus is for giving names to values you were computing anyway. Instead of a storage bin and the value you want to store there, the walrus involves a value and the name you want to refer to it by. Conceptually, the value is where you begin.


That is really really confusing given that there is lots of prior art for = meaning variable binding and := assignment (Algol, Pascal etc). Using = for assignment was a bad decision and it's coming back to bite.

EDIT: Based the examples given, the Python := operator doesn't appear to behave as a let binding. The scoping suggests it is another form of assignment.


It is another form of assignment if you're implementing a Python compiler. The intended use is as a tool for naming values.


I'm trying to understand the semantics not the implementation. The semantics are clearly not "let x = .. in .." as anyone familiar with expression let-bound variables would expect. I wish I could avoid Python but sadly it is taking over the world. It's even taught at schools now and my kids will be learning it as their first programming language.


I have to work with stacks a lot and doing

  while (item := stack.pop()) is not None:
    # ...
is a lot harder to get wrong than

  item = stack.pop()
  while item is not None:
    # ...
    item = stack.pop()
Really wish it was scoped though...

Edit: Just realised you were asking for an if-statement... but I think this is still a valid point.


Scope the assigned variable to the if statement.


that's not how the walrus operator works though.

  if x := 3:
    print("x is now: ", x)
  print("x isn't scoped to the if:",x)


If so, it might be a code smell to consider defining a separate function.


So far, all the suggested usages of := have a code smell about them.


The post's example with reductor seems like a good reason to me.


“Need” feels like a strong choice of words here. A strong point of python is its readable by people who aren’t familiar with it. := has no intuitive meaning


But the intuitive meaning of = and the actual Python meaning of = are completely different. Which is the entire point here.

Indeed, there's no intuitive of "the box that is referred to by the name on the left shall now contain the value that is the result of the computation on the right" since serious humans have had no need to express the concept.

Most people who code in Python have no idea what they're doing. It's all magical incantations that they fiddle with until it gives the output they want. That's why python prohibits

    if x = some_computation() 
since if it permitted it, everyone would be all wtf. But the number of people who can gather that "sequence of symbols taken as a unit" has some arbitrary new meaning almost certainly significantly exceeds the number of people who can gather that "symbol traditionally used for signifying equality" has some arbitrary new meaning.

And, at the very least, if you see some incredible incomprehensible sequence of symbols you can ask what they mean. It is, however, necessary that you have not unknowingly misunderstood what the symbols mean before you can seek clarification, since no-one ever asks for clarification for something they fully but incorrectly understand.


That's it. Symbols, notation, have power. Most programmers don't understand this because they have never been exposed to the idea. It is only natural that people reject it violently when proposed.


:= code examples all seem less clearer. Too easily abused.


How do I type a ←?


If you are inside of Emacs you could change the input method to TeX and type \leftarrow, or press C-x 8 ENTER and write LEFT ARROW, or enable the Compose key system-wide and do Compose + < + -, or copy-paste it, or buy a new keyboard, or curse whoever thought that building a programming language out of modern hieroglyphs was a good idea and then jump ship to Julia. Or something.


Just my 2 cents: I install agda2-mode (official emacs mode of Agda programming language) which supports all sorts of mathematical shortcuts. This way you can type unicode without C-x 8 ing since it's automatic once you type \leftarrow SPACE


I just use the builtin `abbrev` for that. I took the idea from http://home.thep.lu.se/~karlf/emacs.html#sec-7-2 , and defined a bunch of global abbrevs under the `8` "namespace", for greek letters, mathematical symbols, and random unicode characters (a few ASCII ones, too).

One cool thing about `abbrev` is that it can also expand a function instead of a sequence of characters - e.g.,

  ("9ts" "" (lambda () (insert (format-time-string "%Y-%m-%d %H:%M"))))
It's pretty handy when I access emacs from Termux, as I don't have to bother with switching the input method or long pressing (which is an issue even with Hacker's Keyboard, trying to insert `\`).


Incidentally, I was surprised to find an apl keyboard layout installed on my machine by default:

    $ find /usr/share/X11/xkb/ -name '*apl*'
    /usr/share/X11/xkb/symbols/apl
Apparently, it's from XKeyboardConfig[0]. Taking an actual look at the layout file, you can see that there are lots of variants to choose from as well!

    $ sed -n '/xkb_symbols/s/.*"\([^"]*\)".*/\1/p' /usr/share/X11/xkb/symbols/apl
    basic
    common
    unified
    sax
    apl2
    aplplusII
    aplx
    dyalog_base
    dyalog_box
    dyalog_codes
    dyalog

[0]:https://www.freedesktop.org/wiki/Software/XKeyboardConfig


In vim, Ctrl-K < -

":help digraphs" for more information.

(Personally I think that's still too much typing for a common operator.)


Even my least-tech-savvy friends manage to type 25 variations of the yellow smiley face. I'm sure if this were added to the language, Python programmers (and Python editor programmers) would manage to find a way.


By hitting the compose key [1] followed by "<" and "-".

1: https://en.wikipedia.org/wiki/Compose_key


Better yet, you can even define your own combo. Here's how I type it with compose + left-arrow twice:

    $ grep ← .XCompose 
    <Multi_key> <Left> <Left>             : "←"
(I use double press to distinguish from compose, ←, → which I bound to ↔. If you don't need that, "<Multi_key> <Left>" alone will probably be enough.)

You might want to change IM module though, as not all of them work with .XCompose reliably.



Keyboards generate scan codes, not characters. The character translation is all done in software. How do you think input language selection works?


It looks a lot like <-

Coffeescript has had -> and => for a long time.


And Perl long before that.


By copy/pasting it from OP's text like you probably just did :)


no




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

Search: