Hacker News new | past | comments | ask | show | jobs | submit login
K – A simple, fast vector programming language (codeberg.org)
122 points by snicker7 on May 12, 2022 | hide | past | favorite | 110 comments



Since other array languages are being brought up already, here are two things K does that you won't find anywhere else in the APL family as far as I know:

- Dicts (same as hashmaps, associative arrays, etc.) are a native datatype, with `ab`cd!0 1 indicating a dict mapping symbols `ab and `cd to 0 and 1, for example. In K4 and later (ngn/k implements K6), array primitives work on them to do all sorts of fancy things. For example prefix &, "where", returns the indices of any 1s in an array. If given a dict, it gives you the keys corresponding to 1s.

- Partial application. If you have a function f of two arguments, f[;x] binds the second argument x to f, but leaves the first one unbound. So f[;x][y] is f[x;y]. But that's not nearly all: arrays, dicts, and functions are unified, so if fs is a list of one-argument functions, then fs[;x] applies each function to x. This is because fs[;x][i] is fs[i;x] or fs[i][x]!

ngn/k is the best implementation to use now in my opinion. It's short on documentation, but the K Wiki is a new and very nice resource for K. Maybe start with the list of all primitives at https://k.miraheze.org/wiki/Primitives .


I was going to say "wouldn't BQN have dicts since it's trying to be a more accessible, modern, from-the-ground-up designed APL and dictionaries are pretty damn useful?" but then I noticed your username so I guess that answers that.

So now I'm curious: why didn't you include some version these features in BQN? They seem pretty nice to have!

EDIT: will you ever write a "let's learn BQN by figuring out how the self-hosted compiler is implemented!" article? I doubt I'll use BQN soon but I find it a fascinating, extremely interesting language, and I'm just super-curious how you implemented the self-hosting compiler.


Languages with multidimensional arrays (APL, BQN, J, but not K) have trouble with dicts because an index into an array is a list of numbers, and an index into a dict is an arbitrary value. Many primitives, and especially selection, are designed around lists of numbers and don't transfer to dicts. In K, where the index into a list is one number, there's still a requirement that the keys in a dict all have the same level of nesting, but this isn't bad in practice. BQN will eventually have hashmaps implemented as in a more mainstream/conventional way, as objects. There's a model at https://github.com/mlochbaum/bqn-libs/blob/master/hashmap.bq... .

I don't think studying the compiler is a very good way to learn BQN, but I would like to write up parts of it (limited by time and motivation of course). I did some chat sessions on this sort of compilation during early development; see the links at the bottom of https://mlochbaum.github.io/BQN/implementation/ .


> So f[;x][y] is f[x;y].

f[y;x]


Yes. And J has & to bind left or right argument to a dyad making it a monad - e.g. -&5 allows (-&5) 11 which evaluates to 6, while (5&-) 11 evaluates to _6 .


> ngn/k is the best implementation to use now in my opinion

why?


The biggest win for me to use ngn/k is the built-in documentation. K is primarily taught through refcards, and most K refcards that you find in implementations are quite compact, like this: https://kparc.com/k.txt

While refcards like this are perfectly fine, they can confuse beginners at specific parts in the text. This is where ngn/k shines. It has a robust documentation system, containing several help commands for multiple subjects (types, verbs, adverbs, etc.). They also contain examples, have clear separation between subjects, and are way less ambiguous.


https://codeberg.org/ngn/k/src/branch/master/v.c

Where does this madness come from, and why does it infect proponents of array-based languages to the point where they start writing C code in this style!?


I've been fascinated by this style ever since I saw the "J Incunabulum." Unfortunately, it seems to come from idol worship. Arthur Whitney, the author of the K programming language(s) writes in this style, and so those who seek to emulate his achievements sometimes do as well.

Some of my complaints: * true adherence to the source material requires one to dispense entirely with all but the tersest comments * it over-aggressively tunes away whitespace (if you code like this, it's quite beautiful when you align comparable values on the same column between adjacent rows) * many of the idioms rely on undefined behavior, which makes it very frustrating to maintain, port, and extend--to a lesser degree, it makes the code hard to follow, but usually (the most frustrating part of all!) is that the intent is _clear_, it's just that it doesn't work well with the C standard * it emphasizes the placement of as many statements as possible on a single line, which is jarring when those statements don't flow together; on the other hand, the style is quite nice when you allow yourself to place complete and related concepts together, since it's clear that you're "doing something else" on subsequent lines

I said this is "unfortunate" not because I have any problem with Arthur, but because I actually wish this was part of a richer tradition of C (which I've never been able to confirm and thus came to my conclusion about idol worship). Although I happily abide by them in my professional life, I am thoroughly frustrated with coding standards that dictate variable names be complete sentence and code limited to 80 lines. I would love to adopt a rigorous style _based on_ Arthur's, although I will concede that this will probably never happen because the aesthetic of this practice conflicts so completely with the contemporary zeitgeist.

Anyway--nobody has to like it, but some people really actually do like it and accomplish work with it.


> I've been fascinated by this style ever since I saw the "J Incunabulum." Unfortunately, it seems to come from idol worship.

Is that true?

Isn't this style just a natural place to arrive after agreeing with and understanding "Notation as a tool of thought"?

Also while learning J/K I didn't notice much of this worship.

I'm afraid of writing off a purposely chosen style as one that has no merits and is explained by idol worship.


Arriving to your comment late; I apologize.

I want to be clear that I think this style does indeed have merit, regardless of its origins. I like it--I prefer _reading_ and _comprehending_ code rather than superficially skimming code--I believe the style serves that goal wonderfully. I do not think that a thing adopted because of one's respect and adoration for an idol figure invalidates the value of the thing, _but_ I also think I spoke much too harshly and I recognize that people are allowed to like a thing and take it from another person without idolizing the creator.

However, I still believe the style originated with Arthur. Again, I don't think that's bad, and I agree with you that there is indeed some spirit of Iverson's work spread around different computing communities in the way they write C. Then again, _I_ did not have the presence of mind to spontaneously write Arthurian C without reading his code first and thinking really hard about it, so I am perhaps making the mistake that everyone else is similarly limited. I've tried to find independent exemplars of similar styles without success. Again, I write that down as a personal failing.


It's just writing C in the style of the target language. If you can read K, reading that C isn't especially hard.

Some writings on this style:

https://code.jsoftware.com/wiki/Essays/Incunabulum

https://github.com/kevinlawler/kona/wiki/Coding-Guidelines


That does not make me want to learn k.


Learning how to write K is way less painful than learning how to write C in the style of K.


You may want to learn J if you read "J for C programmers" - https://www.jsoftware.com/docs/help807/jforc/contents.htm . A great book.


It’s odd, I seem to remember comments from you on a similar thread about a year ago. https://news.ycombinator.com/item?id=27220613 Are you still shocked or have you just forgotten?


Indeed. But that was in reference to the actual array-programming language. I get that they have their own special syntax, and the terseness helps in a way.

One talk I saw by a proponent explained that the intention is that you often change just the trailing character (or 2 or 3) to "twiddle" a query until it does what you want.

That interactivity makes sense to me. I do similar things with query languages like KQL.

But... in C? It's compiled! You have to "run" it!

Not to mention that it makes no syntactical sense in C to be fiddling around with suffixes. It has nested brackets that need to be properly closed! It's procedural, not declarative, so even if you condense the syntax, you don't "win" that much in the end.

There is literally no way that anyone could ever justify using single character source file names. That's just absurd. Note that the author had to put a little table explaining what each single-character file does/contains.

WHY NOT JUST NAME THEM to match their function!?

So instead of this madness:

    0.c syscalls and main()
    m.c memory manager
    p.c parser
    k.h  embedding api
    a.h  common header
    g.h  header generated by g.k
   
Just use words like a normal human:

    main.c
    memory.c
    parser.c
    embedding.h
    common.h
    generated.h


[flagged]


https://news.ycombinator.com/item?id=31361023#31365022

Ridiculing the value of documentation does not make you look smart.

It rather hints at lack of experience with truly complex systems or collaboration in teams.


Are you saying this is a complex system or that any decisions made in the context of complex systems must be used for all software? Also, this was not in response to a post about documentation but rather a post about the file names. In any event, it says nothing about my experience and you are simply extrapolating.


You can always write documentation in comments. Using terse names for the identifiers when feasible can make it a lot easier to see the bigger picture wrt. multiple components interacting. It's a tradeoff.


Agreed, and it would probably be my preference.

The code referenced here is pretty extreme NOT providing the guidance, hence my response to the poster defending the extreme and ridiculing the good practice.


Because I can already look at a sock and see it's a sock.

If you're keen on this style, perhaps you should pack all the socks, underwear and other objects in your home into identical boxes and put labels like "~" and "z" on them.


Funnily enough, I don’t need a label for the drawer I keep my socks in. None of my drawers have labels. See what I’m saying here?


I do. You're saying you see your code as a personal posession, whose structure you know and understand. You're saying it doesn't matter how easy your code is for somebody else to get their head round because, like your sock drawer, you don't imagine other people will go rummaging around in it.

It's not an attitude I approve of in colleagues, for obvious reasons. Fortunately it's a long time since I've had to work with anyone that thinks that way. They don't survive in workplaces like mine.


Which colleagues are you talking about in this context? In any event I’ve worked for over twenty years in companies with thousands of employees and I can’t remember a single time that the name of a source file made any practical difference in my day. I think the naysayers are having their own conversations distinct from actual reality of what’s happening here. “I wouldn’t do this” is a perfectly reasonable reaction but not at all useful in a public discussion.


I know C and many other programming languages reasonably well, though K not at all.

Are there people (other than the author) who look at this source code and read it fluently? There appear to be few clues as to the meaning, in terms of naming choices (of any identifiers), comments, formatting, etc.


Arthur Whitney created a compiler called b that “compiles a language that is isomorphic to c” (his words) and compiles 1000 times faster than gcc etc. The b language code looked very much like k but was essentially exactly equivalent to a subset of C. I read he abandoned it for other reasons but that would not be a bad way of writing a k implementation! “Same madness”:-) May be ngn or someone else would get inspired to write a translator from b to C.

ngn/k is certainly a lot of fun to use. I just wish it also worked on OS X (& plan9).


> ngn/k is certainly a lot of fun to use. I just wish it also worked on OS X (& plan9).

To start on MacOS, can apply this patch then run `make k-libc` on OSX 12.0.1 (M1 processor):

    diff --git a/makefile b/makefile
    index 5b08dde..1ba3c32 100644
    --- a/makefile
    +++ b/makefile
    @@ -10,7 +10,7 @@ w:k o/w/fs.h o/w/k.wasm o/w/index.html $(patsubst w/x/%.k,o/w/x/%.k,$(wildcard w
     h:w o/w/http;cd o/w;./http
     
     k-dflt:; $(MAKE) a N=$@ R=k  O='-O3 -march=native -nostdlib -ffreestanding'                L=''
    -k-libc:; $(MAKE) a N=$@ R=k  O='-O3 -march=native -Dlibc'                                  L='-lm'                       STRIP=true
    +k-libc:; $(MAKE) a N=$@ R=k  O='-O3 -Dlibc'                                  L='-lm'                       STRIP=true
     k-obsd:; $(MAKE) a N=$@ R=k  O='-fPIC -Dlibc=1 -DSYS_getcwd=304 -Dstrchrnul=strchr'        L='--static -fno-pie -lm -lc' STRIP=true
     libk.so:;$(MAKE) a N=$@ R=$@ O='-O3 -march=native -nostdlib -ffreestanding -fPIC -Dshared' L='-shared'                   STRIP=true
     o/$N/%.o:%.c *.h;$M;$(CC) @opts $O -o $@ -c $<

However, Apple platforms are not officially supported (or to put it more bluntly, "officially not supported"), and there are bugs such as this one[1]. I suspect it has to do with libc differences on Mac versus Linux. If you have experience with the libc differences between Linux and Mac, I'd welcome your input! I'm not an ngn/k developer, just interested in adding support for MacOS.

[1]: https://codeberg.org/ngn/k/issues/30


I have tried something similar to this but its self tests still don't pass. Will give this a try. Thanks!


It's an imitation of Arthur Whitney's "iconic" style.


Identical formatting to the commercial offering.

This is how they roll in the k world. Imagine including k.h in an external application that wants to use some k functionality and the existing code uses the single char looping idiomatic code:

   for (int k = 0; k < 100; k++) {}
'k' gets redefined and LOLs all around.


Well, for what it's worth, the strings with curly braces are K code.


It's very cool to see ngn/k on the HN front page.

After the recent AoC which I've attempted to solve with BQN[1] (another array language) I've got interested in array languages and now exploring K too.

K seems like a nice blend between an array language and a lisp: list is a primary data structure in K, no multidimensional data. K is good at symbolic workloads as well — visit http://nsl.com for some examples.

Also, make sure to check out the C code style ngn/k is written with :-) another K implementation Kona (uses similar C code style) has a wiki page with explanation[2].

[1]: https://mlochbaum.github.io/BQN/index.html [2]: https://github.com/kevinlawler/kona/wiki/Coding-Guidelines


> I've got interested in array languages and now exploring K too.

https://www.arraycast.com/ announced last year (https://news.ycombinator.com/item?id=27209093) may interest a similar audience as this announcement


In case you've jumped straight to the comments, here are some intro links. Many of these also appear in ngn/k's readme.

First, direct links to ngn/k in the browser:

- REPL: https://ngn.bitbucket.io/k/#r

- Editor: https://ngn.bitbucket.io/k/

Second, the best one-stop shop for an overview of k6's primitives (both ngn/k and oK are based on k6). https://github.com/JohnEarnest/ok/blob/gh-pages/docs/Manual....

The best intro examples are in John Earnest's k editor iKe - there's a dropdown at the bottom right. http://johnearnest.github.io/ok/ike/ike.html

ngn/k's editor also has an 'examples' dropdown in its menu.

For an illustration of k's strengths, Razetime's matmul refactoring is brilliant: https://github.com/razetime/ngn-k-tutorial/blob/main/c-think...

Finally, some nice examples of concise k:

  {$[2>#?x;x;,/o'x@&'~:\x<*1?x]}    / Quicksort
  
  (+':,)\~!10                       / Pascal's triangle
  
  {x x}{x{x[x]y}y}                  / Y-combinator
Executable versions of above:

- Quicksort (via Kelas's KCC guide): https://ngn.bitbucket.io/k/#eJwrLLaqVok2slO2r7CusNbRz1evcFBT...

- Pascal (via Richie/Attila): https://ngn.bitbucket.io/k/#eJzT0Fa30tGMqVM0NAAADYMCZg==

- Y-combinator (via ngn): https://ngn.bitbucket.io/k/#eJyLtKquUKiora6oroiuiK2srax14IqM...


If anyone is looking for some additional docs for ngn/k, I’ve written

https://xpqz.github.io/kbook


These style of languages often come up here. Could a proponent give some examples of practical applications written in these array languages? I feel like that's missing in these discussions. People say "finance", but let's say I have a finance team that have never heard of it - why might they be interested?


> why might they be interested?

Because you get done faster.

I have no doubt any experienced C programmer could beat a k solution in run-time performance, but the k programmer will always be done faster.

> proponent give some examples of practical applications written in these array languages?

Databases are the most common: Building an API library to quickly answer questions about data is probably about the easiest thing to do in an array language, but isn't almost anything a database with a definition like that? E-commerce and storefronts, CRMs, flight trackers, and so on. In this way, I often suggest to people that the closest analogue is maybe something like SQL stored-procedures, but with a less-shit language. But this isn't exactly right.

Games are an interesting problem that aren't databases (for example), and a lot of games have vector-problems that have nice solutions in k. So let's play hangman.

If you don't know this game, we will only be playing with lowercase letters:

    A:`c$"a"+!26;  // A gets the characters cast [from] "a" + til 26
First, let's agree on a dictionary to use:

    W:0:"/usr/share/dict/words"
and I will only choose a word with at least 7 letters in it and make it lowercase so you have a chance:

    w:_*1?W@&7<#'W;   // w gets lowercase [the] first [of] 1 random-take [from  the] Words [that/at] where 7< count each Words
Now when you make a guess:

    G:();g:{G::G,x}
I'll now tell you what you know, but you're only allowed N guesses.

    N:5
    k:{@[w;&|/w=/:A^N#G;:;"."]}; // k gets a lambda, [that] amends w where the max-over [when the] word equal [to] each of-the Alphabet without [the first] N [characters] taken from the Guess
That's it. You keep guessing until you guess the word, or you run out of guesses.

    g"e";k[]
    "...e..e..'."
    g"s";k[]
    "..se..e..'s"
It's a silly game. I wrote comments above (with // characters so they should be easy to spot), and I now want you to take a look at them.

The comment is what I "read" when I am reading the k stanza. The square-brackets cover words (prepositions mostly) that help make it sound more like English, but look at the other words too: x=/:y is x equals each-of y. That's all it is. I am reading it and writing it that quickly. Yes it takes longer to write and read the English, and of course, any non-array language will be a lot more code to read and write as well.

That's what I think the value is in an array language: It's just so clear once you know how to read and write it, that you are simply faster. And being clear means the computer does what you expect (usually on the first try!) and so the programmer gets on to the next problem sooner.

I've written ad servers and presentation tools in array languages; I don't think there's anything I can't write (after all, almost everything is a database or a game if you squint hard enough), but there's a lot I wouldn't: I did an MP4 decoder in JavaScript once (to autoplay ads on iphones back when they blocked video playback in iframes) but I would never say JavaScript is the right language to write MP4 decoders. Array languages are tools in your toolbox -- like JavaScript or any other -- and you should try to put as many tools in your toolbox so that you have the best chance of using the right tool for the job.


> let's say I have a finance team that have never heard of it - why might they be interested?

In my experience it's very good at quickly developing real-time analytics applications with only a small set of developers. A couple of q developers can develop, maintain and operate the server side of 5 or 6 separate applications without breaking a sweat. Changes come in at a high speed too.

It's a highly interactive language. A bit like a lisp, you start up a q process, open a port and then you iterate and update your application live without needing to restart. Typically on our projects we've had a well iterated program running in QA for a day or 2 before opening a PR (which becomes more of a formality for getting the solution to the problem into prod at that stage).

The q language itself is quite wordy. Check the reference page: https://code.kx.com/q/ref/ Many programs written in q consist mainly of the key words with the special operators interspersed. Also see some example libraries: https://github.com/finos/kdb

It's been a fairly stable language to work with, having few breaking changes between successive versions. q code written 8/9/10 years ago on older versions will most likely still run the same today. We have source code on one project at work which hasn't had a code change in 6 years now (despite moving through different versions 2.8->3.0->3.3->3.5->4.0) and it runs daily without a hiccup.

Mostly it's a joy working with it because I feel like I get to tell the computer what I want it to do, without also having to tell it how to do it.


How do you think about the k/q split? Do you ever drop into k or use q exclusively?

Is q mostly just longer aliases for k verbs or do the semantics differ?


We stick with q as much as possible. We've found it's better for our front end developers who only touch it a bit here and there to make edits on the APIs they use.

That said we do make heavy use of the functional form of querying tables (https://code.kx.com/q/basics/funsql/#select) in the more complex parts of our systems. The toolbars in the GUIs allow users to select a wide range of possible ways to request data. It's a lot easier to dynamically generate the functional format of the different parts of a select query than it is to try and play with manipulating lots of stored procs. Anyone with experience in a LISP would be familiar with this way of manipulating code as data in this manner, since it's a similar concept.

Some would say this is k, not q and the lines are probably a bit blurred here. Either way there's no question that doing so has made things more productive.

As an aside, there's no performance difference between using k and q. Most of the q keywords are implemented in k. Programs written in k do look quite different to those written in q, even if you stick to the mantra of one thought per expression, one expression per line.

Myself, I used to only like q since it was easier, especially when I started. My only programming background was C# and Python; q made it easy to get into the different paradigm of the APL family. Now, after years working with it, I'd prefer to use k where-ever possible. The brevity is nice. When you write something non-trivial you see a lot of the same patterns of characters on the screen. And it just feels better to write one character for | instead of typing out reverse.

Personally I'd prefer to go with Arthur Whitney's advice from an interview he did years ago (that I can't find a link to unfortunately). He basically said work in k until you need to do big database work with kdb+, then pull in q. Convincing the team and management to let me do that is probably a lost cause though.


The main product is is “kdb+”, a simple, fast, proprietary, embedded, time series database. Especially good for tick data.

I work in finance, and literally all of my employers used it. Not much secret sauce, just simple algorithms implemented very well. No real open source competitor. Well, maybe DuckDB if they support as-of joins.


https://www.dyalog.com/case-studies/financial.htm

An issue in writing a thoroughly helpful response, is that successful APL and K implementations are usually applied in a largely corporate and proprietary context.

I lack a list of applications to link which you could look at.

However I'd still say APL/J/K applications require vastly less resource, and initial investment than most tools whilst providing on-par or greater computational power.


I can offer you the contrary opinion: why I would not use these kind of languages.

A couple of years ago I worked on a non-trivial APL application with one of my university professors and another student. We were trying to build a CPU simulator flexible enough to handle stuff ranging from PDP-11 up to Intel x86. The goal was to run some analysis on memory accesses performed by the x86 architecture. Quite an interesting project in which I worked on for around two year.

The code is still available if you're interested: https://github.com/emlautarom1/PDP_11_Simulator

The first implementation was done in APL using a book which I don't remember as reference. We had a couple of meetings where we learned APL and the general idea behind the design. Pretty soon we started to deal with a lot of issues like:

- We only found two implementations for the APL interpreter: GNU and Dyalog. GNU is free but pretty much abandoned. Support for Windows was (is?) nonexistent. Dyalogs version is proprietary so we couldn't use that (even when a "student" version was available).

- You had to use a special keyboard layout since APL uses non ascii characters, which was only available for Linux. Every time you wanted to write some operator I had to look it up in a paper sheet. Eventually you start memorizing, but it was a pain in the earlier days.

- There is literally no community support. You can't just 'StackOverflow' some APL stuff.

- Dynamic scoping + dynamic typing make working on a large codebase pretty much impossible. You don't know when things are defined, and you don't know what they are.

- The language is extremely terse. I remember that a single line of code was able to fetch, decode and execute instructions. You're incentivized to write this kind of code, and even if you're able to understand all operators you still need to figure out the semantics of the code. For reference, I work with Haskell everyday and it looks like Java compared to APL.

- The code tends to be very hacky. You (ab)use the fact that most things are arrays and solve all problems using some form of indexing/map/reduce. You tend to forget about proper conditionals or custom data types. Domain modeling is pretty much non-existent.

- There is no built in support for test, and we could not figure out how to use a third party library for that (is it even possible to use third party code?). Our tests consisted in a lot of code that checked some conditionals, and then we printed "Pass" or "Fail" on each. Very primitive stuff.

- No debugging at all. You run the code and pray for the best.

A year after we started my classmate decided to drop the project since he felt he couldn't keep up with the complexity: each line of code was non-trivial and really hard to understand.

Eventually we had to rewrite the whole project because GNUs interpreter didn't support big integers, and trying to circumvent that resulted in very poor performance. The new version was written in Julia (https://github.com/emlautarom1/Julia_Simulator), so we were able to reuse a lot of "array code". The project got cancelled in the middle of the rewrite and we kind of forgot about it.


This might have been true a couple of years ago but it is totally untrue now.

I'm not sure why you couldn't use the student version of Dyalog? Sounds like it would have been fine. There are also many more FOSS implementations of array languages now, such as ngn/k and April. https://github.com/phantomics/april

'only available for Linux' - not true https://github.com/abrudz/Kbd/ and others (also different input modes like `w for ⍵)

'no community support' - on the contrary there is a big and helpful APL community https://aplwiki.com/wiki/Chat_rooms_and_forums that is (imo) more useful than stackoverflow

'Dynamic scoping...' - Dyalog's (and other APL's) dfns have lexical scope.

'The language is extremely terse' - is this meant to be a bad thing?

'The code tends to be very hacky' - maybe if you write bad code or try and write C in APL (it won't work)


- My professor was completely against the idea of using proprietary software. Also, we were supposed to use APL, not other languages.

- I see that support has improved. I didn't find any keyboard support for Windows at the time. Still, dealing with a different keyboard layout was a total pain.

- I wouldn't call the APL community "big". The're are around 2K users combining all of those platforms. Now include the language barrier (I'm from Argentina) and go back a couple of years and you can see the issue.

- The APL version we were using had dynamic scoping. TIL that there are multiple variations of the same language with totally different semantics!

- Yes, that level of terseness involves hacky code which is hard to understand, review and mantain. This is 100% my opinion.

Overall I wouldn't recommend APL or derivatives at all for any serious project.


Or derivatives?? Generalizing from GNU APL to APL as a whole is a stretch in my opinion, but telling people not to use K on the basis of your APL experiences is something else entirely. They are very different languages. For example K uses only ASCII and has never supported dynamic scoping, only (somewhat restricted) lexical scoping.


'The APL version we were using had dynamic scoping. TIL that there are multiple variations of the same language with totally different semantics!' - your own code uses localisation within tradfns! Dfns just do that by default.


The original purpose of APL was to describe the IBM/360 system. Kind of ironic, eh?


What makes this implementation different from kona? https://github.com/kevinlawler/kona


The K family of languages hew very closely to versions written by Arthur Whitney who famously rewrites the entire codebase from scratch with each major revision. Kona is largely modeled on K2 (the second major revision) and ngn/k is modeled on K6. K6 introduced a few more types, more flexible dictionaries and added/changed some primitives from earlier versions but the overall structure of the language is very similar.

Someone here has to post ngn’s survey of K languages: https://ngn.codeberg.page/k.html

oK is also modeled on K6 and its manual comes highly recommended.


This particular way of coding to me exemplifies one very important problem with people who work in technical professions: their utter inability to "read the room".

In this specific instance, I suspect the folks who enjoy writing (and assuming they actually can - reading) code like this, completely assume that everyone has a brain with the same kind of weird quirks theirs has, namely the ability to recall almost instantly infernally long minutiae of details and thereby decipher what most people perceive as pure noise.

Or, more generally speaking, they assume that their potential audience essentially are as smart as they are.

News at 11: this is almost never the case.

And in the case of: this type of language, APL, LISP, functional programming, quantum physics, etc ... your favorite topic/programming language/hobby ends up bound to be confined to a tiny bubble of folks that are like you instead of gaining popularity.


If you don't understand the code, probably you don't know what kinds of mental processes are needed to understand it. Maybe "recalling almost instantly infernally long minutiae of details" isn't actually among them. Maybe great intelligence isn't, either.

There's a whole genre on YouTube of videos of people doing day-to-day work with skills that seem utterly superhuman, with names like "Fast Workers #230" and "People are AWESOME 2020", like https://youtu.be/YmkPoiEcgJM. Actually, though, if you practice almost any skill every day for an hour or more, working diligently to improve, after 20–40 years your ability at it will seem superhuman, even if you're average intelligence or slightly below. (Unless it's a professional sport, in which case people will be mentally comparing you to Lionel Messi or Tiger Woods, and you won't get that good. Or a popular hobby like knitting.)

It definitely does not require 20–40 years of effort to understand quantum physics or code written in K.

— ⁂ —

Now, suppose that it were true that reading poetry written in Volapuk (or whatever) were a niche interest because of some essential feature of Volapuk that inherently limits it to a small group, rather than because learning Volapuk takes time that most people haven't put in, for the standard diffusion-of-innovations reasons. Would that be bad? Would it mean that people writing poetry in Volapuk had an "utter inability to 'read the room'"?

I don't think so—to attribute their choice to do so to such a cognitive handicap, you are presupposing that you understand their motivations, and that those motivations include maximizing the popularity of their creation.

But it should be obvious upon a moment's thought that plenty of poetry, and code, is written with no thought for participating in such popularity contests; it's written for an audience of one, or two, or forty-eight. Perhaps the majority of poetry and code. So writing it in Volapuk, or K, may actually be a rational strategy if it appeals to your intended audience—not an error resulting from a mental disability, as you claim.

More generally, quoting Chip Salzenberg:

> It's usually a big mistake to assume that a number of experts on a given subject disagree with you because they don't grasp the true situation.

(cf. http://canonical.org/~kragen/quotes.txt)


> If you don't understand the code, probably you don't know what kinds of mental processes are needed to understand it.

Probably, but then: I spent close to a year of my life writing "software" in APL.

I hated every second of it, especially the part where I'd need to zoom to the bathroom and come back 5 mn later not being able to understand how the last line of code I wrote worked.

I can't even imagine what it would take today, or to the poor soul who inherited the code.

The code I've looked at here reminds me exactly of that time.

Boy would I never go back to doing stuff like this.


There certainly are a lot of people who used APL at one time and then stopped. One of Richard Stallman's first software projects back in the 01960s was a text editor written in APL.

Is it possible the software you were working on was shitty in a way that wasn't essential to APL?


If you haven’t yet, you should check out posts by geocar referred to elsewhere in these comments. Odds are you are selling yourself short if you think the difference is smarts.


It's interesting that in the days APL was rather popular with non-programmers.


I also found https://t3x.org/klong very interesting


ngn has a comparison page for various implementations of K:

https://ngn.codeberg.page/k.html

Note that ktye/i is actually public domain, not proprietary as that page says.


What does debugging code written in this style look like?

Has anyone run code like this under gdb?


I think this style of coding is decidedly less amenable to using a debugger on. With so much on a single line stepping becomes unclear. Heavy use of macros fights against the use of a debugger as well. I think proponents would argue that the extreme rigidity of the style means the code is more likely to fit expectations but you can decide how convincing that is. As for what people actually do, the catch phrase seems to be “use printfs”.


Array languages are very cool. I'm more of an APL guy myself though.


Do they all read like noise[1]?

[1]: https://codeberg.org/ngn/k/src/branch/master/a19/20.k


IMO, readability is simply a misnomer for familiarity, and - for example - a pythoner feels more comfortable reading java than APL purely because it's more regular.

In fact, APL/J/K are simpler to read than most languages![1] Programs consist of functions and higher-order functions that are applied to array(s) in an infix or prefix style. These are evaluated Right-To-Left with minimal precedence rules.

Given a few days, the core language(s) can be easily memorized[2][3][4].

The issue, at it's roots is mostly confidence. And that's a cultural issue APLers are constantly alleviating. A part of that is countering the spread of dishonest framing and narratives.

[1]: https://xpqz.github.io/learnapl/intro.html#but-it-s-unreadab...

[2]: https://awagga.github.io/dyalog/voc/

[3]: https://mlochbaum.github.io/BQN/doc/index.html

[4]: https://kparc.com/k.txt


I hope you have taken some time to understand K's philosophy and goals before making this comment. A tutorial exists here: https://github.com/razetime/ngn-k-tutorial if you'd like to understand why this solution is good.


It was just an honest question. I've just peeked at APL, J and K due to posts here on HN, and the sample code I've seen mostly reads as noise to me, so I was just curious if there were any array languages that would be approachable to me.

The tutorial does help a fair bit, though glossing through it, it doesn't really explain why it has to be so exceptionally terse.

I mean the matmul[1] sure is impressively short in the end, and with a background in computer graphics and simulations I can absolutely appreciate working on arrays rather than singular values. But I'm sure one could get the same computational result with a bit more approachable syntax.

[1]: https://github.com/razetime/ngn-k-tutorial/blob/main/c-think...


> the sample code I've seen mostly reads as noise to me ... But I'm sure one could get the same computational result with a bit more approachable syntax.

Chinese looks very strange to lots of people, but I think you should expect to get a strange look if you were to suggest Chinese reads like noise to you and then suggested, gee, you know, if they just used latin letters for everything, you would still "get the same computational result"


Where was I saying the Chinese should use latin letters?

And the fact that you can go on Google Translate and go from Chinese to English does indeed suggest you can generally get the same computational result from either language. Sure some things expresses better or more naturally in one or the other, that's to be expected.


> Where was I saying the [array languages] should use [more accessible symbols]?

I know you do not see these the same, but maybe that will help?

> you can go on Google Translate and go from Chinese to English

Yes yes. You can use arrays with python. Whoopdy-do. But you're mistaken if you think you understand what "叶公好龙" means just because you put it into Google Translate; This is Darmok and Jalad at Tanagra. You have no hope when you're decoding every character and translating it into "accessible symbols" you know, and even if you can put up with it and puzzle it out with a little extra googling, you already know you don't like to do that, so why are you doing it?

I'm telling you to put up with what you're calling "noise" and learn it anyway. Don't skim it and try to "pick it up as you go" like you've undoubtedly tried and been somewhat successful at in other languages. It won't work. You will have to change. Then at some point, you won't be decoding or deciphering or "working it out" anymore, you'll just be reading. It will be much easier to talk about the benefits/weaknesses of k's choice of symbols at that point.

If you want some ideas about how to go about learning this stuff, I'm happy to give some pointers if I know a little better about what you already know and where you're at.


Translating a K expression into something much more readable won't likely help you as the language is extremely compact. So for example, translating "ab",/:\:"cd" to ((each-left (each-right concatenate)) "ab" "cd") will still be inscrutable to you. But you can experiment with k, one expression at a time, play with its builtin operators and incrementally build up knowledge. Long before you get really proficient in it, you will start appreciating its compact syntax.

Without trying you wouldn't know if you are going to love it or hate it. But at the same time, there are far too many interesting things to learn so one has to pick and choose. Ignore what the cool kids of the day are doing or the latest fads. Just do what furthers your personal goals!


Well, yes, there's other languages like Julia, MATLAB and such if you want familiar, approachable syntax for array programming. Nial (https://nial-array-language.org/), which is inspired by APL, mostly uses words instead of symbols, which you may find more intuitive.

In K, terseness is part of the package, and it is considered good, because you can see the innards of your code at a glance, there's lesser text to change if you mess up, so on, so forth. Some time investment in the language may or may not help with this.


Or, or, and here me out here, you could learn the syntax in order to make a more informed assessment.

Do also seek out geocar’s posts. They are well written and to my mind convincing. https://news.ycombinator.com/item?id=27393682


> you could learn the syntax in order to make a more informed assessment

I'm fairly sure my assessment wouldn't change even if I did learn the syntax. It would still read like noise. The only difference would be that I could understand the noise.

I've read and worked on enough super-terse code in other languages to feel confident in that.

> Do also seek out geocar’s posts. They are well written and to my mind convincing.

I guess it comes down to how ones brain is wired then. For example, I have the exact opposite reaction to multiple pages of code vs terse one-liners compared to geocar[1]. I don't have trouble remembering what range() does because it says so right on the tin, unlike for example + in k (addition or, surprise, transpose).

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


> I guess it comes down to how ones brain is wired then.

Your brain is not wired. You can learn new things, including this.

> I've read and worked on enough super-terse code in other languages to feel confident in that.

To know you will fail before you try? What a great time-saver that must be. I simply would never believe it.

Listen, I don't think most k code is "super-terse" -- for sure, it is fun to play golf with k and think about ways to make it shorter, but you can do that in any language. That's not what's going on here.

Allow me to explain: I learned k because I saw someone do something with k I did not know how to do with C. That was it. That convinced me that there was value here, and so I learned k.

And then something magical happened: When I went back and read C, I was reading that faster. Not quite as fast as k, but substantially faster than I was reading C before.

That was so surprising and unexpected to me. I think I expected that if I took a break from C I would get rustier at C, not better. And so I talked to a few people about it, and convinced them to learn k, and you know what was really amazing? It made them better programmers too!

So I hear that you can't now, and that's okay, but I think it's worth learning. I don't know if everyone learns differently, but most everyone (myself included) struggles and struggles and struggles until one day, they just get it. And it's hard to know how far away you are from that point unless you go there, so if you're worried you're not smart enough to figure it out, you just need to trust that dumber people than you have figured it out and you just have to keep trying. I am telling you it's worth it.

There are other languages that actually try to look like noise, like brainfuck, where part of the challenge (or the treat) is figuring out how to say things we think of as simple using the most primitive of tools, but k is not one of them.

I remember the first thing that got me excited, was when I realized the implications of (intentionally!) confusing indexing from application. In k, f[x] means if f is a function, call it with x as an argument, but if f is an array, give us the value at index x. Just think of this pattern, in all the ways you see it:

    for(i=0;i<x.length;++i)f(g[x[i]]);
    x.forEach(function(a){f(g[a])})
    foreach($a as $x){f($g[$a]);}
    for i in range(0,len(x)): f(g[x[i]])
Now they're all just f g x; barely 10% of the typing and code! I just can't agree that f g x is "super-terse" that's just the normal way to write it. I also think it is easy to agree that it doesn't look like noise and couldn't possibly be anything simpler.

> I don't have trouble remembering what range() does because it says so right on the tin, unlike for example + in k (addition or, surprise, transpose).

Try to read a little more carefully what I am saying: I wasn't confused about what range() did, I was confused about whether it was the best way to do what I wanted to do.

Also, + is flip not APL's transpose operator.


> To know you will fail before you try?

I don't of course. But I don't have infinite time or resources, so I sadly have to prioritize. Hence why I was curious if there was something more accessible.

> I just can't agree that f g x is "super-terse" that's just the normal way to write it.

I agree that allowing indexing and function calls to have a unified syntax can be very useful. I've used this several times in my own code (through anonymous functions).

But I still think "f g x" is quite terse. For example, it's suddenly implied you're looping over all the elements of x, whereas if it was just function composition it would return a function taking an index.

Does it in itself look like noise? Not to me. But with the extensive use of symbols and tendency to do one-liners it adds up.

Anyway, appreciate the discussion. Maybe I can get a chance to crack the code, so to speak. Regardless of how I think it looks, it does seem like an interesting language to know.


> it's suddenly implied you're looping over all the elements of x,

One of the most mindblowing parts of array languages is that there are no loops. Everything is just arrays and operations composed to produce more arrays. A few weeks ago, I was firmly in your camp, and thought APLs were indecipherable, but they're really not. It's just a totally different philosophy of programming.


Yeah I'm sure there's a good reason for it. But I think most would still call it quite terse.

Anyway, good to know it might not be as impenetrable as it appears. Just glanced at the "Notation as a Tool of Thought" article and it looks illuminating as well, so will be reading that in detail.


> implied you're looping over all the elements of x, whereas if it was just function composition it would return a function taking an index.

I think it's a mistake to make this assumption.

In C, if you saw:

    f(g(x));
you wouldn't expect to be able to make any assumptions about how many loops are going on in there. Python programmers are often overloading their functions too, so you can't even say anything about x+y in Python. I actually think it's the norm that you have to dig all the way down to understand what's going on, and it's the same thing happening here: Yes either f or g might be a loop. Or both. That's just life.

At the end of the day, the only thing I think that helps with that is having less code to read (in source-code bytes).


> I think it's a mistake to make this assumption.

I think you might have misunderstood me. However since I don't know k well enough, maybe my point was based on a misunderstanding as well.

What I was trying to get at was that, as you also note, overloading makes it harder to keep track of what the code does. Sometimes this terseness is worth it, and that might be the case for k and friends once you know it.

> the only thing I think that helps with that is having less code to read

I don't have experience with k, so things might be different there. But certainly for me, it is not true in say Python or C# etc where I find less code can be significantly more difficult to read and understand. Of course that doesn't mean writing highly verbose code is automatically better, the optimum is somewhere in between.

For example, often it's just as important to know why some code does what it does, so splitting up operations, using more verbose with variable names etc can make the code significantly more self-documenting.


> certainly for me, it is not true in say Python or C# etc where I find less code can be significantly more difficult to read.

Is it possible this is only a reflection of your experiences and not reality? Surely when you say this, some example sprung to your mind, but where is the proof?

> it's just as important to know why some code does what it does, so splitting up operations, using more verbose with variable names etc can make the code significantly more self-documenting.

I doubt the importance. Quite seriously. When I want to know why the business asked for it, I ask them; There's probably a ticket or a email someplace.

But I have zero interest in puzzling about what the programmer was trying to do[1], and let me tell you why: I think it is very easy for our minds to be tricked into thinking we read something when we actually didn't. If you've never seen Paris in the the spring, you might not know what I mean, but perhaps you've seen some close-up magic, and maybe you've even been fooled before.

I think the only things I need to concern myself with are what the program actually does, and how that differs from what the business wants.

I have on more than one occasion gotten off a long-haul flight just to read someone's code out loud to them over their shoulder. I'm not kidding. The bug is there. I absolutely do not care one bit what the comment says above there, or what you think that library function should do; the code doesn't do what the comment says, it does what the computer says! The computer doesn't know the difference between a context or a ctx (or a c).

Of course we do, and so when we see it, we assume we know what it is, instead of checking. And when we get it wrong? Bang, another bug, and I'm having airplane gin for supper.

[1] I am being somewhat dramatic here. I'm quite interested in watching people use my libraries so I can understand the way that I help, and I'm quite interested in looking out for training or tooling opportunities, so if people keep making the same mistake, it's probably worth some investment. I mean for the purposes of making changes the business wants.


> Is it possible this is only a reflection of your experiences and not reality?

For terseness, I think it's pretty easy to show that k is a lot more terse than say Pascal in various quantitative ways.

Readability quite subjective though. I'm not saying k is universally less readable. But my experiences tell me that for me it might be. Hence why I was looking for alternatives that might fit me better.

> I think the only things I need to concern myself with are what the program actually does, and how that differs from what the business wants.

This is exactly what I was trying to get at. For me, self-documenting code helps with this, as I find it easier to find discrepancies or where I need to focus my attention when extending code.

Anyway, got to spend a bit more time on the two tutorials posted here, which really helped, so will try to take a stab at k during summer break.


> Readability quite subjective though

> I'm not saying k is universally less readable. But my experiences tell me that for me it might be.

I would not worry too much about this: Readability as we can make statements about the code has an executive function[1], but as it pertains to you, how readable it is to you only affects your ability to use these tools of thought, and that will only improve by doing more of it.

If you've learned another spoken language after childhood, it is a little like that, and some people get stuck (or plateau) at some point for a long time and think they'll never get any better, but suddenly one day they do, and I'm sure you can do this too. It won't help to say more about readability, for what will be will be. What's important is to commit to an open-mind and leave open the possibility you can be convinced.

Maybe if you can find a local meetup or a group of array-nerds to talk to, you would find it helpful to join them; I've noticed many people "get" things easier when they see and hear lots of different explanations about what's going on (I certainly feel that way about my own learning!), and when you see people at different stages of their journey, you might learn more about your own. But if not I'm always happy to help; you can drop me an email, and I'll tell you what I think.

[1]: That is, for management, Readability is quite important: It impacts how easy it will be to replace your developer.

> Anyway, got to spend a bit more time on the two tutorials posted here, which really helped, so will try to take a stab at k during summer break.

Boa sorte!


I think you’re right that if you’ve already decided what the outcome will be then it’s not worth your time but also think it severely undermines the value of your opinion for others.


I have a feeling it's a bit like being color blind. It doesn't help how much people try to explain just how different red and green look if I'm red-green color blind.

Ever since I got somewhat proficient at programming, I've been able to take a quick glance at some non-terse code with descriptive identifiers and get a feeling for what it's doing. Often very accurately, which makes it easy to mentally filter the code into what I need to focus on and what's irrelevant to the task at hand.

But if people write very terse code, this goes down rapidly. I have to spend a lot of time scanning for important symbols, unpacking what's going etc. When they stuff everything on as few lines as possible and use single-letter, non-descriptive identifiers it gets significantly harder to read for me. Now I really have to study the code, and remembering what things do gets harder due to non-descriptive names.

I've worked on a fair bit of really terse code, and I find it just doesn't allow me to be as productive as I am with less terse code.

This is why I doubt that learning k will completely change that, and given that I have very limited time and capacity for fun things like learning new languages, I was curious if there was something more approachable for me.


I think very few would argue that this style of coding doesn’t demand more of the developer, but the thinking is that the focus it demands is appropriate. Programs are written for running, ideally accurately. Without focus you can’t be 100% sure the code you glossed over wasn’t important. Hopefully though this style allows you to spend your valuable attention on the bits that matter.


> the sample code I've seen mostly reads as noise to me

Would you rather write/read:

DIVIDE X BY 5 GIVING Y

or

y=x/5; / this would be considered "noisy" per your definition, relative to COBOL

The first is COBOL (designed to make code easier for "normal" people to read. The second is C/Java/Python/Javascript (which looks more like the math that we learn in grade school).

k/APL/J simple moves further in the direction of the algebraic notation you already know. The difference is more operations/algorithms.

When you read the one-character symbols in K as algorithms versus characters, it makes much more sense. In addition, you can read "faster" in k than in other languages, relative to the functionality being expressed.

When I review C/Java/C++, I print out the source code and write the equivalent k code in the margin. The compression ration is typically 10-20X. Doing so speeds up my work significantly when I go back over the reviewed code.


> so I was just curious if there were any array languages that would be approachable to me

q is a very wordy language that sits on top of K4. Some open source examples of libraries used in finance:

https://github.com/finos/kdb/tree/main/q


APL is pretty approachable. You just gotta put a few hours in. There is a tryAPL website that lets you type in some simple programs. It's funny, but I've only spent a few hours on APL, and can still remember the symbols I learned after putting it down for a year.


If you want to understand the thinking, read Iverson's Notation as a Tool of Thought. These languages are optimized for the user rather than the learner, so the curve is steep but rewarding.


Reads like music to me


At least music sheets have a clear structure, I don't see any in the examples like the one I linked to. Of course it could be that the examples I've seen just happen to be written like IOCCC entries.


You can't see structure in a language that you don't know? I mean, sure, one possibility is that there just isn't a structure, but that's really not the obvious option here.


I can see structure in most new programming languages I'm exposed to. I might not immediately understand what the code does, but usually there's some sense of rhyme and reason.

Though most languages are not as terse as this.


These languages are from a different branch of the computer science language tree. The verbs are different. The same verbs usually work similarly for all data types.

Each of those symbols (verbs) are jam-packed with meaning, and most looping is implicit or very natural, and overall code constructions are very terse.

It's like reading a nice short poem rather than a 700 page book.

It can go too far, of course; there is some middle ground to be found.

If you have time, reading into the language a little more deeply may be illuminating, because it changes the way you think about the very nature of the task at hand.


Aaron Hsu has a bunch of talks on YouTube that cover this. He argues that his compiler that translates APL to run on GPUs (or something like that), is much more coherent than the versions he wrote in scheme and other languages. It's easy for him to see at a macro level, what the code is doing. There's no need for boilerplate either. Pretty cool stuff.

Note that I'm not advocating that everyone should do APL for all uses. I adore Python and think it's awesome. I'm just saying these languages deserve more love than they get.


In some of the example programs written in KAP (my APL derivative), I tried to write it in a style that makes people unfamiliar with the array style more comfortable.

This code could of course have been written in a style similar to some of the more extreme examples, and they would have been significantly shorter in that case.

https://github.com/lokedhs/array/blob/master/demo/advent-of-...


Thanks, that does look a lot more approachable.


Ever used dc(1) in Unix?


Never heard of it till now. Interesting, reminded me of my mom's HP-15C calculator I played with as a kid.


Swiss micros sells high quality remakes of many of the HP calculators, with a couple of high end new creations of their own. Works of art and engineering for sure. Not as good as my top end TI for just getting stuff done though.

My dad has a 15c as well. Fond memories there.


Speaking of works of art and engineering, the three HP Voyager series calculators on my desk (12C, 15C, 16C) still work as well as they did several decades ago when they were new. As does the other 15C that I regularly carry around and occasionally drop.


My father only had a problem with the battery leaking in a bigger HP calculator that had these expansion modules. His 15c is also still working since the 80s.


I wonder what a diff looks like on code like this? Is it even a good idea to use a diff on something that dense rather than just re-sending the entire code?


By default git diff works on entire lines, but try this:

    git diff --word-diff=color --word-diff-regex=.
It highlights only the parts that have actually been changed/added/deleted.


I think a diff is just as valuable with this code. Of course understanding a diff also requires understanding the code but if you’re interested the repository is linked at the root of this thread so you could poke around.


Yes. The diffs tend to be quite small & obvious.


Cool to see another open source flavor of q, but is there an open source alternative to kdb+?


The author of Kona has written kerf which I believe is intended for this same space. I’ve never used it though. https://github.com/kevinlawler/kerf1


kerf is not open source


You’re right. I don’t see any license mentioned in the source at all.




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

Search: