Remix.run Logo
xelxebar 11 hours ago

> Subordination of detail

The paper doesn't really explore this concept well, IMHO. However, after a lot of time reading and writing APL applications, I have found that it points at a way of managing complexity radically different from abstraction.

We're inundated with abstraction barriers: APIs, libraries, modules, packages, interfaces, you name it. Consequences of this approach are almost cliché at this point—dizzyingly high abstraction towers, developers as just API-gluers, disconnect from underlying hardware, challenging to reason about performance, _etc._

APL makes it really convenient to take a different tack. Instead of designing abstractions, we can carefully design our data to be easily operated on with simple expressions. Where you would normally see a library function or DSL term, this approach just uses primitives directly:

For example, we can create a hash map of vector values and interred keys with something like

    str←(⊂'') 'rubber' 'baby' 'buggy' 'bumpers'             ⍝ string table
    k←4 1 2 2 4 3 4 3 4 4                                   ⍝ keys
    v←0.26 0.87 0.34 0.69 0.72 0.81 0.056 0.047 0.075 0.49  ⍝ values
Standard operations are then immediately accessible:

    k v⍪←↓⍉↑(2 0.33)(2 0.01)(3 0.92)  ⍝ insert values
    k{str[⍺] ⍵}⌸v                     ⍝ pretty print
    k v⌿⍨←⊂k≠str⍳⊂'buggy'             ⍝ deletion
What I find really nice about this approach is that each expression is no longer a black box, making it really natural to customize expressions for specific needs. For example, insertion in a hashmap would normally need to have code for potentially adding a new key, but above we're making use of a common invariant that we only need to append values to existing keys.

If this were a library API, there would either be an unused code path here, lots of variants on the insertion function, or some sophisticated type inference to do dead code elimination. Those approaches end up leaking non-domain concerns into our codebase. But, by subordinating detail instead of hiding it, we give ourselves access to as much domain-specific detail as necessary, while letting the non-relevant detail sit silently in the background until needed.

Of course, doing things like this in APL ends up demanding a lot of familiarity with the APL expressions, but honestly, I don't think that ends up being much more work than deeply learning the Python ecosystem or anything equivalent. In practice, the individual APL symbols really do fade into the background and you start seeing semantically meaningful phrases instead, similar to how we read English words and phrases atomically and not one letter at a time.

jonahx 10 hours ago | parent | next [-]

To rephrase crudely: "inline everything".

This is infeasible in most languages, but if your language and concise and expressive enough, it becomes possible again to a large degree.

I always think about how Arthur Whitney just really hates scrolling. Let alone 20 open files and chains of "jump to definition". When the whole program fits on page, all that vanishes. You navigate with eye movements.

DHRicoF 10 hours ago | parent | prev [-]

> k v⍪←↓⍉↑(2 0.33)(2 0.01)(3 0.92) ⍝ insert values > k{str[⍺] ⍵}⌸v ⍝ pretty print > k v⌿⍨←⊂k≠str⍳⊂'buggy' ⍝ deletion

I like your funny words. No, really, I should expend some time learning APL.

But your idea deeply resonate with my last weeks struggle.

I have a legacy python code with too much coupling, and every prior attempt to "improve things" went adding more abstraction over a plain wrong data model.

You can't infer, reading the code linearly, what methods mutate their input objects. Some do, some don't. Sometimes the same input argument is returned even without mutation.

I would prefer some magic string that could be analyzed and understood than this sea of indirection with factories returning different calculators that in some instances they don't even share the same interface.

Sorry for the rant.