
A Shallow Introduction to the K Programming Language (2002) - kick
https://web.archive.org/web/20070519112242/http://www.kuro5hin.org/story/2002/11/14/22741/791
======
beagle3
Note this is about k2; the current production version is k4 (from 2006 or so),
and the current prototype is k9/shakti (There was now k8 afaik - possibly to
avoid k8s mixup? but there were k5, k6 and k7 protoypes).

Compared to k2, modern k9 adds a lot of atomic data type (integer bit sizes up
to 128; 32 bit floats, time and date, uuids, ...); adds dictionaries as a
first-class compound data type (K2 had extremely limited dictionaries); rolls
the database into the langauge (k2 had it as a library+app), adds a lot of
connectivity (web server and client including websockets), threads, various
forms of multiprocessing

and drops the native GUI.

------
amelius
> K also has a the concept of dependencies and triggers. They are used for
> efficient, demand-based calculation of data and executing callback code
> whenever a value is changes (this is how the timer system in K works). K
> will keep track of out of date dependencies for you and only do the minimal
> amount of work necessary to update values.

This seems powerful. Is "doing the minimal amount of work" provably true, or
is this just a way of saying that it's usually fast?

For example, let's say that I have a list of numbers that I have sorted using
bubble sort. Now I change one of the numbers. Would the update be O(log N)?

> K has a unique, declarative GUI subsystem that is based around the K tree
> and triggers. It takes a minimal amount of work to put a GUI on your
> application. K takes the approach that you show variables and changes made
> to that variable on the screen are immediately reflected in the program
> without any work by you.

This sounds like Svelte avant la lettre. I'm really curious how the updates
are performed.

~~~
beagle3
> This seems powerful. Is "doing the minimal amount of work" provably true, or
> is this just a way of saying that it's usually fast?

It is powerful, it's not provably true (or even generally true ...)

"Dependencies" are Excel-style recomputation. The code c::a+b says (1) that c
depends on the value of a and the value of b, and that (2) whenever you
evaluate c, if 'a' or 'b' has changed since the last evaluation, you re-
evaluate the expression and assign it to c; otherwise return the cached value.
So, multiple changes to a or b will not trigger recomputation - only the
eventual use of C; However, it's possible that 'a' is a vector of length
1,000,000 and so is b, and only one element in a was changed - K would still
do a million addition operations.

"Triggers" work the other way arround: You used to set a trigger on 'a' by
assigning a trigger function a..t:{[d,i] ...} which would receive as argument
the post-assignment value 'd' and the indexes assigned 'i'; You could use
these to do a minimal update of whatever logically depends on a; However, the
triggers DO fire on every change unlike dependencies.

> For example, let's say that I have a list of numbers that I have sorted
> using bubble sort. Now I change one of the numbers. Would the update be
> O(log N)?

I think there is no way to guarantee O(log n) anyway in this case - change the
first element to be in the middle - requires at least O(n) changes on either a
sequential or singly/doubly linked list. But assuming you're talking about
say, a heap structure - the answer would be "No" for a dependency, and "If you
implement it yourself" for a trigger.

> This sounds like Svelte avant la lettre. I'm really curious how the updates
> are performed.

Sort of. It was a native GUI, incredibly fast, and incredibly bare bones - it
was eventually dropped because all the customers were making web front ends
anyway.

Being a native GUI, I suspect (but don't know) it was using the triggers to
mark damage regions and let the windowing system take over from there.

------
tonetheman
I had great feels just now when I looked at this page. I had forgotten
kuro5hin and it came rushing back.

There were lots of good articles on that site...

~~~
kick
Weren't there? There really hasn't been as interesting a site since.

~~~
gwf
It was the best. RIP kuro5hin.

------
smabie
K is the bee's knees! I'm convinced if we all just used k for all of our
software, programming would be a much nicer endeavor. And as a corollary,
programs would be a lot nicer, faster, and usable. The status quo of terrible
performance, massive codebases, and staggering complexity is a sad, self-
inflicted, and unnecessary state of affairs.

~~~
WillDaSilva
I'm an avid user of k myself, but let's not pretend that k doesn't posses
numerous issues and questionable design decisions. As is, the language is well
suited for some tasks (e.g. processing timeseries data) , but certainly not
all. The lack of modules as a first class feature makes it difficult to design
large k projects, and hurts the discoverability of community made modules. A
related issue is that namespaces/contexts can't be nested. Another thing that
always feels like a thorn in my side when I'm working with k is the type
system. Dealing with enums and anymaps is a nightmare, and the dynamic type
system with no type hints makes creating and using k APIs difficult. I could
go on, but I'd rather not stay up enumerating what I believe the bad parts of
k are.

I believe that the ideas in k can be built upon into something that would be
more useful as a general purpose programming language, but for now most of the
benefits that k has to offer come in the form of introducing developers to
vector languages. The ease with which functions can be composed, projected,
and applied to vectors and tables in k is remarkable. For this reason, and the
many other benefits of k, I try to get my programmer friends to try it out. I
honestly believe learning it makes one a better programmer.

~~~
kick
_The lack of modules as a first class feature [...] hurts the discoverability
of community made modules. A related issue is that namespaces /contexts can't
be nested. Another thing that always feels like a thorn in my side when I'm
working with k is the type system._

All of these read more like benefits than drawbacks, speaking for myself.

~~~
saagarjha
How do you organize complex programs?

~~~
kick
Good question!

You're generally quick to ask questions in any threads of any APL variant, so
I'll answer for both it and k:

Workspaces are the answer for APL. This one is generally more-agreeable to
your average person.

Here's a quick explainer of them, from a now-dead implementation:

[http://www.microapl.com/apl/apl_concepts_chapter2.html](http://www.microapl.com/apl/apl_concepts_chapter2.html)

However, the mindset for k doesn't really align with 'complex programs':
simplicity is generally more useful than complexity, and it's not like there's
a difference in how much you can get done with either. Consider this two-page
IM implementation:

[http://nsl.com/papers/instant.htm](http://nsl.com/papers/instant.htm)

Now, it's obviously just a silly demo, but in how many other languages could
you get a graphical client and server in two pages of code? (Alternatively, in
five lines but not graphical:
[http://www.kparc.com/$/chat.k](http://www.kparc.com/$/chat.k) )

Or a graphical spreadsheet program in one:

[http://nsl.com/k/s/s.k](http://nsl.com/k/s/s.k)

I'm not trying to imply that these are particularly-complex programs, but I'd
argue that in most cases, trying to organize programs more than what's obvious
(generally a directory) just makes them more complex for no benefit. This goes
for many languages, not just k.

Consider Apter's SLACK, a SASL-like (direct Haskell/Miranda precursor)
functional (k) programming language, written in k:

[http://www.nsl.com/k/slack/](http://www.nsl.com/k/slack/)

You'll notice that it's pretty complex as far as these things go, but the
organization of it is far less complex than most programs of a similar level
of complexity. I'd argue that it actually works to the benefit of the reader,
though I can see how you might disagree.

~~~
saagarjha
To be clear, I'm not sure I could ever call an inability to write complex
programs a feature, but I do recognize that there are many useful languages
with this property; Bash comes to mind. Thanks for the links, by the way; I am
sure I'll get a kick out of them when I eventually do know how to read k ;)

~~~
kick
It's not an _inability_. It's just that there's no reason for complexity.

C++, for example, is a language built around managing complexity. k, on the
other hand, is a language built around _avoiding_ complexity. SLACK is an
actual language! It does all of the things you really need an actual language
to do! And yet the implementation's not particularly complex.

bash isn't really at all similar; bash is actually _more_ like Swift or
Javascript than k is like bash. bash is for _extremely_ complex programs:
programs that are other programs taped together.

k takes the reverse position: there's absolutely no reason you should ever,
ever, _ever_ create something complex if a simple solution will do.

If you can create a programming language, an operating system, a windowing
system, a database that can handle millions of terabytes of data without
flinching, etc. all without complexity, why would you opt for the more-complex
solution?

Complexity kills manageability, clarity & fun. Simplicity allows you to do the
same things, faster, and without sacrifices.

EDIT:

In some ways, you can consider this very similar to Chuck Moore's philosophy
on programming, although taking a different approach to it. I'm paraphrasing
here, so forgive me if I get the quote wrong, but something along the lines of
'FORTH will do anything you could possibly want, but it won't do everything'.

If you accept the premise that a language shouldn't be infinite, which isn't
particularly controversial[1], it becomes much less of a leap to think, "Wait,
why not allow the programmer to expand where they need, and just not throw in
what they don't? It'll take less overall cognitive load!"

Now, that's why FORTH is pretty interesting as a language. k, on the other
hand, doesn't really lend itself to being expanded (there are solutions to
expanding the language itself; many people & companies do). However, it takes
the same philosophy externally rather than internally: "We're not going to
give you everything, we're going to give you a method of creating anything in
a highly-efficient way."

An analogy that I think works fine: k is LEGO to your average programming
language's pre-built kits of Lincoln Logs.

[1] This is why Lisp is dead (Common Lisp killed it) and C is still going
strong, despite the former being great (except Common Lisp) and the latter
being...highly-controversial, and why when a simple Lisp was released it blew
up for quite a while despite absolute mismanagement (Clojure).

