Live data from Hacker News

What learning APL taught me about Python

mathspp.com

91–100 of 104 posts

Re: What learning APL taught me about Python

#91
post #14

I feel like this kind of operation on a list feels more naturally expressed by filtering the list and taking the length of the filtered list. Like this line of JS feels so much easier to read than that line of python: ages.filter(age => age > 17).length Directly translating this approach to python: len(list(filter(lambda age: (age > 17), ages))) Although a better way to write this in python I guess would be using lis…

> Obviously if the function is on a hot path iterating and summing with a number is far more efficient versus filtering. I got curious and checked this in Rust, the generated assembly is the same! https://rust.godbolt.org/z/jhGWdYPz1

Neither of your examples is doing what the parent suggested as an alternative, which is simply looping and counting rather than creating an anonymous function to repeatedly call. I doubt this makes an enormous difference in practice, but you can see your generated assembly doing all the argument passing and stack frame setup, which is extra instructions compared to just straight looping and counting.

Re: What learning APL taught me about Python

#92

Earlier quoted context omitted.

And how many Baud do you type at today?

I've gotten paid for working with APL code. Math professors who aren't great at typing love that stuff, but code has to be maintained and some mild verbosity, as python has, is a very reasonable price to pay for that maintainability. If this was punch card input, or 110 baud teletypes, where program listings come back at a snails pace and use paper, then APL is great for that.

> "If this was punch card input, or 110 baud teletypes, where program listings come back at a snails pace and use paper"

So if your typing speed hasn't gone up in proportion to the increase in Baud, I'm guessing your reading speed also hasn't gone up tens of thousands of times, and your ability to hold working state in your head hasn't gone up thousands of times, what is the advantage of increased Baud to code readability which you are talking about?

Let's say I'm not disagreeing, but I'm trying to dig into what specifically the change is which makes the difference; the computer can display more code at you per second than 1950 but humans can't read much faster than 1950 so that doesn't seem like it will help. Presumably longer books aren't inherently more readable than shorter books?

Can it be that Python is more readable because it lets you skim over and not read more of the code? Since not-reading isn't reading, it seems like 'more filler' that you don't read isn't what adds to readability.

It presumably isn't that Python is more English-y because languages which tend towards English words (SQL, Objective-C, PowerShell Cmdlets, Applescript, BASIC) are often maligned specifically for that reason, and because Python isn't English - you couldn't speak it to Shakespeare and have him understand you).

It presumably isn't because Python uses fewer symbols, or we'd all love to write Java style var1.Equals(var2) instead of == and var1.Plus(var2) instead of + and people seem to dislike that also. Why would + be preferred over .plus() but .sum() be preferred over +/ ?

Is it that Python has more visible structure to hang understanding on? Is it that it's more like walking compared to jogging compared to sprinting, that one can sustain a lower effort 'slower read' for longer?

Re: What learning APL taught me about Python

#93

Earlier quoted context omitted.

It is summing but being used for counting (in imitation of the same style from APL) via punning on True/False as 1/0. Not what actually happens but conceptually: ages = [17, 13, 18, 30, 12] sum(age > 17 for age in ages) => sum([False, False, True, True, False]) => sum([0, 0, 1, 1, 0]) => 2 # via conventional summing Since True and False are 1 and 0 for arithmetic in Python, this is just a regular sum which also happe…

yeah if i ready the line using "sum", I would be expecting the result 48 (18+30) not 2

Using the same format, sum((age > 17)*age for age in ages), perhaps.

Re: What learning APL taught me about Python

#94

Earlier quoted context omitted.

You're saying that brevity for typing on 110 baud teletypes was the primary reason for the density of APL operators, but that's not historically true. For one thing Iverson and pretty much everyone else high profile in the APL community has said that the conciseness was itself a major part of the power of APL. Further proof of that: APL began as a mathematical notation on chalkboards, and only later was it decided to…

Writing on a chalkboard is another situation where brevity is at a premium. There's limited space and the act of writing on it is slow and causes hand cramps if it's too verbose.

Fair point, but even taking that without argument, it's still not about 110 baud teletypes. :)

Re: What learning APL taught me about Python

#95

Earlier quoted context omitted.

The blog starts out with mentioning other reduce operations in Python: 'prod from the math module; min; max; any; all; and "".join'. In APL those are: ×/ product reduce ⌊/ min reduce ⌈/ max reduce ∨/ logical OR reduce (any bool set) ∧/ logical AND reduce (all bools set) ,/ catenate reduce (join without spaces) These all show a pattern of connection clearly where the Python names don't, that they are related operation…

Interestingly most (if not all) python examples also work in julia. Instead of using sum one can also use count, which might be more readable: julia> count(age > 17 for age in ages) Or even shorter: julia> count(ages .> 17) Under the hood many of the functions are implemented using the reduce function (similar to / in APL): julia> reduce(+,ages .> 17) I think many languages in data science have been influenced by APL…

Yes. One of the “repercussions” of the author’s experience with APL might have led to him recoiling from the verbosity of his Python examples and exploring languages, like Julia, that can get closer to the APL spirit.

Re: What learning APL taught me about Python

#96

Earlier quoted context omitted.

b = lambda: (a := 2, print(a)) Hmm, need to put the assignment before. Can't access nonlocals unless you only read the the var and don't write to it. That's the way functions in python work as well.

> Hmm, need to put the assignment before. No, that would not have the intended behaviour: your `print(a)` is reading a fresh local variable, defined by the `:=` expression (which shadows that from the outer scope), so it will output '2'. The intended behaviour is to print '1' and reassign the outer name; but we can't do that (that's why I chose it as an example!) > Can't access nonlocals unless you only read the the…

I merely explained why it didn't work, for any passerby. I don't think the rules are hard to understand once described. There's basically only one of consequence—if writing outside the current scope there neeeds to be a clarifying statement first to avoid ambiguity. Unfortunately that prevents usage in a lambda.

However, I don't think complex functionality belongs in them either, so not a big loss. See the Beyonce rule mentioned elsewhere in this thread.

Re: What learning APL taught me about Python

#97
post #23

Earlier quoted context omitted.

If you're going to go that route, I think this makes more sense: count_over_17 = [age > 17 for age in ages].count(True)

For a very large sequence traversing it to build a list and then traversing the list to do something you could do in one traversal without creating a list may be undesirable.

In a very large sequence, you would be using numpy. I assumed the purpose of their example was some sort of clarity, over `sum(age > 17 for age in ages)`, rather than performance.

Re: What learning APL taught me about Python

#98
post #18

Earlier quoted context omitted.

numpy (which is inspired by Matlab which is inspired by APL) does indeed have a count_nonzero function, which is intended to be used in situations like this. Unfortunately, it (like most of numpy) doesn't work with generators, just array-like objects (aka numpy arrays and python lists), so it has the same memory performance issues as filtering and using len. If your input was a numpy array to begin with you could ski…

Here, is "broadcasts" like apply or map of functional programming?

Yes, very similar. When performing an operation between an array and a scalar, it is identical to mapping that operation on each element of the array. Broadcasting generalizes this to also handle operations between matrices and vectors, such that the operation with the vector is applied to each row or column of the matrix.

Re: What learning APL taught me about Python

#99
post #14

I feel like this kind of operation on a list feels more naturally expressed by filtering the list and taking the length of the filtered list. Like this line of JS feels so much easier to read than that line of python: ages.filter(age => age > 17).length Directly translating this approach to python: len(list(filter(lambda age: (age > 17), ages))) Although a better way to write this in python I guess would be using lis…

The concise reduce is fairly small:

    ages.reduce((a, c) => (a + (c > 18)), 0)
Though usually in production code you'll see this as something like

    ages.reduce((acc, cur) => {
      return acc + c > 18 ? 1 : 0
    }, 0)
Which creates some more noise but ups readability

Re: What learning APL taught me about Python

#100
post #60
post #14

I feel like this kind of operation on a list feels more naturally expressed by filtering the list and taking the length of the filtered list. Like this line of JS feels so much easier to read than that line of python: ages.filter(age => age > 17).length Directly translating this approach to python: len(list(filter(lambda age: (age > 17), ages))) Although a better way to write this in python I guess would be using lis…

Doing @WalterBright's job for a second to let you know that in the D language, given a function that is defined as taking, say, a list as the first argument, it can be called either myfun(list) or list.myfun() and this applies without exception. This means a lot of C-library code gets easier to read when used from D: Vector3 vec = (Vector3){ 1, 2, 3}; Vector3 result = Vector3Add(vec, vec2); becomes auto vec = Vector3…

I love D, learned it back in high school, but unfortunately it just seems like D is one of those great languages that just isn't going to take off. It just doesn't have a big enough champion to bootstrap a thriving large community that would make more people want to learn it and more companies adopt it.

On its technical merits, one of the best languages out there. It gives you insane performance but is so much easier to learn and write than C++ or Rust.

Post reply on HN