Dyalog / MDAPL

The de facto standard for people who are looking to learn Dyalog APL from a book. This updated version is a work in progress.
https://mastering.dyalog.com/
Other
30 stars 7 forks source link

Should the book be first-axis oriented? #16

Open rodrigogiraoserrao opened 2 years ago

rodrigogiraoserrao commented 2 years ago

The original book is mostly last-axis oriented, e.g. preferring / over when working with vectors. Should we change that?

jjtolton commented 1 year ago

I'm not sure what the argument for over / is, but coming from a functional background, reduce or map over a "list of rows" or "list of lists" is a fairly common operation. So / matches typical iteration order, whereas is a very, very unusual iteration order for most non-array-oriented programmers in other languages I work with.

For instance, in Python,

# +/3 4 ⍴ ⍳12
>>> list(map(sum, [[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]]))
[10, 26, 42]

is a fairly familiar operation whereas

# +⌿3 4 ⍴ ⍳12
>>> list(map(sum, zip(*[[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]])))
[15, 18, 21, 24]

is much less familiar.

(zip(* ... ) inverts "rows"/"columns" of list of lists).

So it might make sense to start there, depending on the audience.

abrudz commented 1 year ago

There's no (explicit) map here, only reduce. The plus reduction of [[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]] should be [1,2,3,4] + [5, 6, 7, 8] + [9,10,11,12] while keeping in mind that + auto-maps, so we get [1+5+9, 2+6+10, 3+7+11, 4+8+12] i.e. [15, 18, 21, 24].

jjtolton commented 1 year ago

I think I might be using APL terminology incorrectly? What I'm trying to say is that

      +/3 4 ⍴ ⍳12
10 26 42

matches a Pythonic notion of

In [22]: result = []

In [23]: for items in [[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]]:
    ...:     result.append(sum(items))
    ...: 

In [24]: result
Out[24]: [10, 26, 42]

or, equivalently,

>>> map(sum, [[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]])
[10, 26, 42]

So from my perspective, / seems like it is more relatable to new APL users than . Of course, I'm a fairly new APL user too, and still learning to think in tensors, so maybe it is better to start with for reasons I don't understand yet?

(sidenote: @abrudz, I'm a big fan, you are an absolute machine and it's exciting to interact with you for the first time!)

abrudz commented 1 year ago

Ah, but that's really the wrong way to think of it. Let's define APL's + and as

plus = lambda a,b:[plus(x,y) for x,y in zip(a,b)] if type(a)==list else a+b
reduce1st = lambda f: lambda a: functools.reduce(f,a)

Now reduce1st(plus)([[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]]) gives the expected [15, 18, 21, 24] while [10, 26, 42] is the result of list(map(reduce1st(plus), [[1,2,3,4], [5, 6, 7, 8], [9,10,11,12]])).

Therefore, +⌿ (reduce1st) is the more fundamental operation, and / can be defined in terms of it. Indeed, / is nothing more than ⌿⍤1 (a mapped reduce1st).

jjtolton commented 1 year ago

tl;dr Great point @abrudz -- I wish MDAPL went deeper into things like this -- not just the how to but the underlying why

That is a GREAT point. I had not considered that reduce1st was the more fundamental operation than reduce. It's things like this that still stun me about APL. In fact, I would like to offer that I think insights like this are the only short coming of MDAPL -- I feel like there should be a sequel or some additional insights into the deeper thinking behind it.

For instance, in the classic CS curriculum, one of the foundational schools of thought is lambda calculus -- which we are taught is equivalent to a turing machine (aka an infinite length vector) -- and that one of the first things we learn to construct from pure functions is a cons or pair, and then from nesting pairs we learn to make a linked list. We are then taught that associative data structures are made by creating a list of pairs, and "objects" and polymorphism are made from functions that "pass messages" to specially constructed associative lists, an so on. And I've gone on to repeat this to my students as time goes on.

However, I suspect based on my learning of APL that tensors are a mathematically equivalent way to represent general computation. However my first reading of "Mastering Dyalog" felt like a teaser -- that there are many truths and proofs like the one @abrudz just presented -- that are only hinted at.

It also seems like array-oriented programmers are largely partitioned off from the rest of devs so it's hard to bump into that information by accident. So for instance, it's hard to understand what non-trivial day-to-day applications would look like. For instance, writing a web server or parsing a text document -- but it's clearly possible. Not only that, the underlying algorithms for things like the magical inner product function are not treated in MDAPL.

Given the importance of tensors to ML/AI, I am really surprised there is not a lot more research and training invested into array oriented programming. In some of the scientific computing circles we're making efforts to understand tensors better and some early experimental efforts and incorporating APL into the language, but I feel like I'm poking a very elegant and complex machine with a stick to try to understand how it works. Besides MDAPL, I'm not even sure where to go to get deeper insight!

Anyway thanks for listening to my TED talk and all the great work on MDAPL!

jjtolton commented 1 year ago

I'll offer one more point, which is that languages like Python and Clojure have long and detailed notes about why the language was designed in certain ways. For instance, Python has its PEPs and in Clojure we can't shut up about the why's of the language, and Rich has a great history on it. And I can tell that APL and Dyalog had a LOT of thought put into them and some of the decisions were made very carefully. It would be so great to be able to dig more into that thought process.

abrudz commented 1 year ago

I think APL Wiki can be a better resource for the _why_s. Feel free to add a section for Reduce based on the above.