├── SUMMARY.md ├── img ├── k.png └── kei.png ├── LICENSE └── README.md /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [+/∞](README.md) 4 | 5 | -------------------------------------------------------------------------------- /img/k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/module/kcc/master/img/k.png -------------------------------------------------------------------------------- /img/kei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/module/kcc/master/img/kei.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 regents of kparc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # +/∞ 2 | 3 | plus over infinity / 𝒌 crash course 4 | 5 | **[genesis](#genesis)** 6 | 7 | * 𝒌 → [ø](#ø) | [who](#who) | [why](#why) | [wha](#wha-) | [how](#how) 8 | 9 | **[exodus](#exodus)** 10 | 11 | * [get](#get) | [run](#run) 12 | * style → [cmt](#annotations) | [sep](#separator) | [tab](#indentation) | [ids](#identifiers) | [space](#space) | [bad](#bad-form) 13 | * parlance → [xyz](#implicit-arguments) | [rank](#rank) | [proj](#projections) | [+:](#explicit-monadics) | [scope](#scope) | [nouns](#on-verbs-and-nouns) 14 | 15 | **[numbers](#numbers)** 16 | 17 | * vector math → [v≠a](#vectors-vs-atoms) | [v+v](#v-plus-v) | [v+a](#v-plus-a) | [v x](#v-indexing) | [shp](#v-shp) 18 | * [type system](#types-of-types) → [num](#typ-num) | [char](#typ-char) | [name](#typ-name) | [date](#typ-time) | [dict](#typ-dict) | [tab](#typ-tab) | [λ](#typ-lambda) | [ø,∞](#typ-nul) | [mix](#typ-mix) | [cast](#typ-cast) 19 | * order of eval → [rtl](#right-to-left) | [(1)2](#precedence) 20 | * [no stinking loops](#no-stinking-loops) → [over](#nsl-overscan) | [scan](#nsl-overscan) | [e↣](#nsl-each) | [e↔](#nsl-eachlr) | [e↤](#nsl-eachprior) 21 | 22 | **[proverbs](#proverbs)** 23 | 24 | * reading code → [how to solve it](#how-to-solve-it) 25 | * writing code → [three triangles](#three-triangles) 26 | * metrics → [apples and oranges](#apples-and-oranges) 27 | * learning plan → [gladly beyond](#gladly-beyond) 28 | 29 | > quod tu summum putas gradus est — seneca 30 | 31 | --------------------- 32 | 33 | ## genesis 34 | 35 | ### ø 36 | 37 | Computer languages have been around for a long time, but in the beginning was the Word. We will be writing code in a language called 𝒌, but it helps to talk about it first. 38 | 39 | 𝒌 is different. At first, you will be questioning its design, and it will respond by questioning things that you consider common sense, but soon enough it will become a constructive conversation, and here is how. 40 | 41 | The first thing newcomers frown upon is why the assignment operator is `:` instead of `=`. But before you close the tab, try a simple thought experiment: 42 | 43 | ```java 44 | x = x + 1 45 | ``` 46 | 47 | Most programmers will agree that this expression makes perfect sense, but if you show it to a math guy, be ready to hear "no, it isn't". And once you see what makes him think that way, you will also see why we assign values with `:` 48 | in k. The above expression looks nonsensical to a 𝒌 programmer for the same reason it does to a math guy, and k will most always evaluate it to false. Once you can produce a 𝒌 expression where `x=x+1` evaluates to true, please submit a pull request and say "hello". 49 | 50 | 𝒌 offers some unusual perspectives compared to other programming languages, and they are not necessarily wrong. They are simply different - in the same way that C++ is different from Java, and Python is much unlike Javascript. 51 | 52 | This short introduction invites you to look at those things that other way, and we hope they'll come across as obvious and natural to you, just like `x≠x+1` did a few seconds ago. 53 | 54 | --------------------- 55 | 56 | This crash course is not looking to make you an expert 𝒌 programmer, because that takes a lot of time and effort. Instead, it aims to give enough confidence and motivation for you to continue on your own. We value your time, so we promise it will be fast and violent: 57 | 58 | * The text cuts a lot of corners on general programming and CS at some expense of readability. 59 | * The course is driven entirely by densely annotated code, comments contain essential material. 60 | * New syntax is often introduced inline, some is self-explanatory, some relies on your intuition. 61 | * The narrative is linear, all chapters build on previous. 62 | * Skipping exercises will halt your progress. 63 | 64 | This might feel a bit intense, but we hope the course is still lightweight enough to be completed in one session. 65 | 66 | This document is **not a 𝒌 reference**. The majority of subjects are discussed at depth sufficient to give a good general overview, but by no means exhaustive. 67 | 68 | ### who 69 | 70 | The man behind 𝒌 is a computer scientist by the name Arthur Whitney. He is the principal designer of the language, and he is an iconic figure in a community of some of the most sophisticated programmers and scientists employed by some of the most influential institutions on the planet. Since early 90s, he delivers ever more powerful revisions of a concept he has been refining throughout his career, a system to build very efficient software that transforms large amounts of data into large amounts of money. 71 | 72 | That is, 𝒌 enjoys much success in the world of finance, where this kind of problems existed long before the term **big data** was coined. Many people embraced the 𝒌 way and made successful careers by building solutions using 𝒌, and they appreciate their tool as much as they appreciate the man behind it, and we believe they have their reasons. 73 | 74 | ### why 75 | 76 | There is a good chance that you have heard or read about 𝒌 language — a lot of people know the story — but what is much less likely is that you have ever met a professional 𝒌 programmer. This happens not just because 𝒌 programmers are rare, but also because 𝒌 is not fishing for cheap publicity. This is how we've all heard about a language called 𝒌, but what we mostly get to hear about is how much it sucks to be a Java programmer. 77 | 78 | But jokes aside, implementations of similar systems in languages like C++ or Java usually involve thousands of lines of code written by large teams, built on top of complex library stacks and even more complex infrastructure. Such projects are often expensive and inflexible, go over budget and miss deadlines. 79 | 80 | In comparison, 𝒌 solutions are typically a few factors of magnitude less code, implemented by small and agile teams, rarely require external dependencies, and ship on time. 81 | 82 | At first it could be hard to understand how this can even be true, but imagine the effort of keeping 100 lines of code in sync with rapidly changing requirements and free of bugs, compared to 10,000 lines of code that do the same thing. Against all intuition, it is not 100 times easier, but 10,000 times easier, due to a devastating effect of cyclomatic complexity. 83 | 84 | ### wha _*_ 85 | 86 | 𝒌 is a simple, expressive and powerful computer language. 87 | 88 | Its power stems from the fact that 𝒌 is designed as a *tool of thought*. The vocabulary, syntax and the choice of abstractions offered by the language drive you to think about problems in a focused and clear way that quickly takes you to efficient and elegant solutions. And the reason why thinking in terms of 𝒌 is so effective is nothing supernatural: brevity is the soul of wit. 89 | 90 | 𝒌 programs are concise, the syntax of the language is terse, and there is no boilerplate code to write. In 𝒌, most of the time is spent on thinking about the problem rather than writing and refactoring code or browsing source. 91 | 92 | > \* no, this is not a typo 93 | 94 | ### how 95 | 96 | 𝒌 runtime environment is an incredibly compact and efficient piece of software. 97 | 98 | The entire system is: 99 | 100 | * a single binary executable 101 | * without any external dependencies 102 | * that fits in the cache of your CPU 103 | 104 | And that gives a selection of fundamental algorithms, data structures, techniques and primitives that withstood the test of decades of production use in some of the world's most demanding data processing environments. Inner components of the system fit together and complement each other to deliver *performance*. It is not uncommon for 𝒌 newcomers to experience shock when they first see how much can be done with a few precise keystrokes, and how fast. 105 | 106 | All of 𝒌 programming takes place in the **REPL**, an idea that is actually much older than many seem to think. It has been around for at least half a century, and is known as *dialogue approach*, a live conversation between a human and machine as a flow of questions and answers. And in 𝒌, this conversation is much more fluent than in any other modern REPL-driven system you may be familiar with, because the questions are short and the answers are fast. This is the essence of the way of 𝒌, an experience that all 𝒌 programmers consider immensely satisfying. People who write 𝒌 for living love their jobs. 107 | 108 | ## exodus 109 | 110 | The only known way to learn how to program is to write programs, so you will need a live environment. As all things 𝒌, it takes very little effort. 111 | 112 | ### get 113 | 114 | We will be using a trial version of 𝒌, which comes without any RAM or useable CPU core limitations. As any 𝒌 programmer will tell you, with great power comes great responsibility, so please don’t accidentally convert too much data into too much money too early. 115 | 116 | The 𝒌 interpreter is currently available for `Linux` and `macOS` on `x86_64` architecture, but stay tuned - ARM, WASM, RISC-V are in the pipeline, and they will not make you wait. 117 | 118 | So, no further ado: 119 | 120 | ```sh 121 | $ npm i @kparc/k -g 122 | ``` 123 | 124 | As all things 𝒌, the development of 𝒌 itself is happening very fast. New builds are published up to several times a week, so make sure you always use the latest version: 125 | 126 | ```sh 127 | $ alias kup="npm i @kparc/k -g" 128 | $ kup 129 | ``` 130 | 131 | ### run 132 | 133 | Start your very first 𝒌 session like so: 134 | 135 | ![alt text](https://github.com/kparc/kcc/blob/master/img/k.png?raw=true "k prompt") 136 | 137 | The startup banner packs a lot of useful information: 138 | 139 | 140 | | it says | it means | 141 | | :-------------------|:------------------------------| 142 | | 2019-08-23 12:18:26 | timestamp of your 𝒌 build | 143 | | 40core | cpu cores you can use in 𝒌 | 144 | | 270gb | max workspace alloc size | 145 | | avx512 | the best your cpu can pull off| 146 | | shakti | the company behind 𝒌 | 147 | | l | `m` for macos, `l` for linux | 148 | | prod | your build is `test` for now | 149 | 150 | > If it ever comes to that, please always include your banner in your bug reports. 151 | 152 | At any time during a 𝒌 session, you can: 153 | 154 | `\h` view k reference card 155 | 156 | `\l` view k change log 157 | 158 | `\\` quit k session 159 | 160 | At this point we highly recommend to avoid issuing any of the above commands, especially the latter. 161 | 162 | --------------------- 163 | 164 | **Practice:** 165 | 166 | Make sure you have `rlwrap` utility installed, and put an alias `alias k="rlwrap k"` into your rc file. This makes your spartan 𝒌 development environment a lot more pleasant to use. 167 | 168 | Type in your first 𝒌 expressions, and enjoy your first answers: 169 | 170 | ```q 171 | 2+2 /simplest face of k is a calculator 172 | 4 173 | 174 | x:42 /: is assign so x is now the answer 175 | x=x+1 /= is equal, and of course it isn't 176 | 0 177 | 178 | kcc:+/∞ /we knew you'd want to try this one 179 | kcc /and what it happens to be is just: 180 | █ 181 | ``` 182 | 183 | > Wherever you see ∎ in this document, you are invited to try something on your own. 184 | 185 | Indeed, the title of this document seems to make sense to the 𝒌 interpreter and evaluates to exactly that, and very soon you will easily infer what it actually means. It is quite profound. 186 | 187 | --------------------- 188 | 189 | ### remarks on style 190 | 191 | As any other language, 𝒌 expects a programmer to observe certain conventions on coding style and terminology in order to understand the code written by the others and let their own code be understood. While some rules of the house of 𝒌 are universal, some are not. 192 | 193 | #### annotations 194 | 195 | Commenting your 𝒌 code is the best way not to end up coding Java for food, unless you are Arthur Whitney. We dare to assume you are not, so comments start with `/`. When used inline, prepend at least one space. Here is an annotated declaration of two variables: 196 | 197 | ```q 198 | /annotations are your friends 199 | x:42 200 | 201 | y:42 /now, always and forever 202 | ``` 203 | 204 | #### separator 205 | 206 | Character `;` in 𝒌 is used for one thing and one thing only, to separate 𝒌 expressions. As you have seen above, 𝒌 doesn't force you to terminate the line explicitly with `;` because **newline is also an expression separator**. Separator is used the same way and means the same thing everywhere in any context (except comments), e.g. to separate expressions inside a function body, vector declaration, function arguments, etc. Later we will see that separator is also a part of certain language constructs, but it has the same meaning there as well. But by far the most frequent explicit use of the separator you will encounter in the wild is to separate expressions within one line: 207 | 208 | ```q 209 | x:1; y:2; z:3 /one line, three expressions 210 | x:1;y:2;z:3 /denser version of the above 211 | ``` 212 | 213 | #### indentation 214 | 215 | This is a tricky subject in 𝒌. Basically, what you generally want is **no indentation**. This means if your 𝒌 expression is getting so large that you are tempted to split it into separate lines, you likely need to refactor or return to the blackboard. Sometimes, however, indentation is fine and even necessary, and it is always *one space*. Not two, not four, one. Tabs will be frowned upon because they eat up a lot of *space*, see below. 216 | 217 | #### identifiers 218 | 219 | Variable names in 𝒌 follow a somewhat unusual convention. Capitals are used by 𝒌 programmers very sparingly, which applies both to code and comments. While identifiers in `camelCase` can sometimes be tolerated, `c_style` identifiers are not permitted at all, since `_` is an operator. Identifiers of functions and variables are very often boiled down to an absolute minimum, names 1-3 characters long are commonplace, which does not impact readability given that their definitions are annotated. Short identifiers might sound like a bad idea to Java programmers who are used to see identifiers longer than 2^8 bytes, but, unlike Java, 𝒌 source requires very little or no scrolling. When the entire program fits in your visual buffer, "cryptic" identifiers are no longer a problem because their annotated declarations are always right in front of you: 220 | 221 | ```q 222 | kei:42 /kenneth eugene iverson 223 | ``` 224 | 225 | #### space 226 | 227 | The subject of code organization will forever remain a major point of controversy in software development. Computer languages have different philosophies on coding style, especially when it comes to three specific keystrokes: **`\n`**, **`\t`** and, less surprisingly, **`0x20`**. If we define two extremes as "tall, lean, sparse and readable" and "robust, wide, dense and cryptic", then C is a classic example of `tlsr`, and 𝒌 is all the way down `rwdc` road. 228 | 229 | * The primary influence of 𝒌 syntax is the most powerful instrument of human reasoning, *a mathematical notation*. As such, the language actively encourages the programmer to produce very dense and succinct code, which leads to a common pitfall among beginners who are tempted to sacrifice too much readability. As any good program, a good 𝒌 program must remain readable and adequately annotated. 230 | 231 | * Comments are integral part of the code and also consume space, so boil them down to some reasonable size as well, but don't get too cryptic. 232 | 233 | * In 𝒌, it is possible to minimize code scrolling or even avoid it completely. When the entire program or component fits in your view, you lose no time on navigating it and switching contexts. For example, every code block in this document fits on a laptop screen and remains very readable on mobiles (try that with Java). 234 | 235 | * Syntax highlighting is essential, and poor highlighting is often worse than none — so choose carefully from 𝒌 syntax packages available for your editor. The best is often the one you wrote yourself, and 𝒌 syntax is extremely regular and simple. 236 | 237 | * Medium is the message, so we refer you to 𝒌 code presented in this document. Please send pull requests to help us improve it, and if you like the style, it is yours to have. 238 | 239 | #### bad form 240 | 241 | Bad form in 𝒌 is code bloat. Avoid writing extra code if you can — there is too much of it written already into the world. Remove inessential code, yours or not. But if you absolutely have to write more, make it useful, secure, compact, maintainable, portable and scalable. 242 | 243 | -------------------- 244 | 245 | **practice** 246 | 247 | We don't know much 𝒌 to practice style yet, so this one will be read-only. Here is a trivial C program formatted in two different ways: 248 | 249 | **tlsr:** 250 | 251 | ```c 252 | #include 253 | 254 | int 255 | main(int argc, char **argv) 256 | { 257 | int x = 'a'; 258 | 259 | for(int i=0; i<26; ++i){ 260 | putchar(x++); 261 | } 262 | 263 | return 0; 264 | } 265 | ``` 266 | 267 | **rwdc:** 268 | 269 | ```c 270 | #include //k.h 271 | typedef int I; 272 | #define O putchar 273 | #define DO(n,x) {I i=0,_i=(n);for(;i<_i;++i){x;}} 274 | ``` 275 | 276 | ```c 277 | #include"k.h" 278 | I main(){I x='a';DO(26,O(x++))}//nsl 279 | ``` 280 | 281 | Compare their strengths. 282 | 283 | 284 | ### remarks on parlance 285 | 286 | The most important terminology in 𝒌 revolves around functions. Functions in 𝒌 are first-class citizens. 𝒌 has anonymous functions, eval, apply, recursion, and then some. It takes a leap of faith to believe it, but 𝒌 is probably more lispy than certain Lisps, only you don't need to get past any parens. However, since there are no linked lists under the hood, 𝒌 is _not_ Lisp, because it was designed to be fast. 287 | 288 | #### implicit arguments 289 | 290 | This is an uncommon feature, most languages require you to explicitly declare function arguments. Of course you can also do that in 𝒌 if you want to, but if you don't, a function can have up to three implicit arguments called `x`, `y` and `z`, which means you declare them by simply referencing them in the function body. It is a very convenient feature, not nearly as scary as it sounds: 291 | 292 | ```q 293 | f:{x+y+z} /f[] takes three arguments 294 | f[1;2;3] /and here is how to call f 295 | 6 296 | 297 | e:{[a;b;c] /not so implicit, but why? 298 | a+b+c} 299 | 300 | e[1;2;3] /f with 9 extra keystrokes 301 | 6 302 | 303 | f:{x*x} /f[] has only one argument 304 | f 42 /and you can omit brackets 305 | 1764 306 | ``` 307 | 308 | Note that when calling a function with three arguments `f[1;2;3]` we had to use square brackets and use an expression separator, because each argument passed to a function is an expression in its own right. However, second function only takes one argument, and we were allowed to omit brackets — although we could also say `f[42]`. 309 | 310 | This illustrates the core principle of 𝒌 syntax — almost everything that you intuitively feel you should be able to omit, can and should be omitted. Top candidates for omission are square `[]`, round brackets `()` and space `0x20`. The less you type, the better your code will get. 311 | 312 | Syntax for explicit argument declaration `{[a;b]}` is just a side remark. It is good to know, but we won't see it in this text again. 313 | 314 | #### rank 315 | 316 | Function rank is another way of saying *valence*, a fancy word that describes a simple idea that is extremely important to be understood well. Rank of an operator or a function is basically the maximum count of arguments they take. Two functions shown above have ranks of 3 and 1, respectively. 317 | 318 | Two specific ranks are so important that they have their own names. A function or an operator that takes... 319 | 320 | * one argument is **monadic** 321 | * two arguments is **dyadic** 322 | 323 | As you will see, the vast majority of native operators in 𝒌 have exactly two completely different meanings based on the context where they are used, which is in turn defined by the number of arguments offered to the operator. 324 | 325 | For example, when you used your first ever 𝒌 operator in the expression `2+2`, you have used the `+` operator in a dyadic context since it received exactly *two* operands to work on, left and right, so it was inferred to be `dyadic x+y plus`. The `monadic +x flip` will be introduced later, and has an entirely different meaning. 326 | 327 | #### projections 328 | 329 | Also known as *function views*, projections can be understood as "incomplete" function calls with at least one free, or **elided** argument. For example, if a function of rank 3, `f[x;y;z]`, only receives first and third arguments, it will return its monadic projection, which itself behaves as a function: 330 | 331 | ```q 332 | f:{x+y+z} /a function of rank three 333 | p:f[1;;3] /a projection of f[1;?;3] 334 | p 2 /is same as call f[1;2;3] 335 | 6 336 | ``` 337 | 338 | Projections are commonplace and very useful. To give one example, projection provides a way for a lambda or a function to capture some of their *context*, i.e. bind values from the current scope to the local scope of a lambda, wherever it travels next. In other words, if you prefer functional speak, projection creates a **closure**. 339 | 340 | #### explicit monadics 341 | 342 | As you already know, the action of a 𝒌 operator depends on the number of arguments passed to it. However, there are situations when an operator receives two operands (typically, left and right), but is intended to perform a monadic action using right argument only. To disambiguate the rank, the operator can be declared **explicitly monadic** by appending `:` to it: 343 | 344 | ```q 345 | + /context-aware, either monadic flip or dyadic plus 346 | +: /always stays a monadic flip, disregarding context 347 | ``` 348 | 349 | Later on you will see how this works in practice. 350 | 351 | > You will not get far in this course without a strong grip on the idea that some things in 𝒌 land are **monadic** and even **explicitly monadic**, while others are **dyadic**. Make sure you have it. 352 | 353 | On a more general note, functions in 𝒌 can be of rank 1 to 9: 354 | 355 | * it is not really possible to define a function with no arguments. Rank zero, or *niladic* functions, do not exist in 𝒌. 356 | * a function cannot take more than nine explicit arguments, and some say this is an overly generous limit. 357 | 358 | #### scope 359 | 360 | Variable scoping in 𝒌 is an important aspect of its design. Newcomers often expect to find so called **lexical scoping** in 𝒌 — that is, every inner scope has access to all variables defined in all outer scopes, which is how the majority of modern programming languages treats this subject. However, 𝒌 has a different take on this. 361 | 362 | In 𝒌, variable visibility is limited to exactly two scopes: **local** and **global**. 363 | 364 | Take your time to absorb this fact and appreciate its implications. While not easily digested by imperative crowd, the benefits of scope isolation are immediately obvious to functional folks: 365 | 366 | * While 𝒌 is not _purely_ functional, for as long as 𝒌 function does not access or modify global state, it remains _pure_, i.e. free from side effects. Pure functions may behave in a mathematically sound fashion, and can be reasoned about in terms of *domain* and *range*, much like their math cousins. 367 | 368 | * Scope isolation relieves the program from an entire class of bugs related to shadowing and naming clashes. 369 | 370 | * Pure functions are best friends with immutability and distributed architectures. 371 | 372 | The fundamental benefit of this way of thinking about functions is **simplicity** and **composability**. Code blocks that are easy to debug, test, refactor and reuse result in clean, secure and scalable systems. 373 | 374 | #### on verbs and nouns 375 | 376 | The last remark on 𝒌 terminology is of extreme importance. While 𝒌 is a computer language, its **grammar** is defined in terms we normally use to denote parts of human speech. That is, 𝒌 expressions are composed of _verbs_, _nouns_ and _adverbs_. For now, let's focus on the former two and consider the following two sentences: 377 | 378 | * "shuffle this deck of cards" 379 | * "take three random cards from this deck" 380 | 381 | Both are imperatives, where verbs act as the **main** part of the expression - they identify the _action_ to be taken. However, there is an important difference between the two. In linguistics, the structure of the first sentence is known as _verb-only predicate_, while the second is a _verb-plus-direct-object predicate_. In 𝒌 speak, we recognize the verb "shuffle" in the first sentence to be monadic, while the second is built around a dyadic verb "take". 382 | 383 | And this is exactly what **𝒌 verbs** are: 384 | 385 | * A verb instructs the interpreter to do something, or answer some question. 386 | * A verb can be monadic or dyadic, i.e. requires one or two nouns to form a grammatically correct phrase. 387 | 388 | Since verbs _operate_ on nouns, they are very often called **operators**, and nouns passed to verbs are said to be their **operands** or **arguments**. 389 | 390 | For newcomers, the biggest caveat with verbs is whether or not to consider a **user-defined function**, i.e. any expression enclosed in curly brackets, to be a verb — and the correct answer is **no**. This might feel counter-intuitive at first, but it helps to think about the content of curly brackets as of an independent expression, i.e. a sentence of arbitrary complexity, which can contain as many verbs as needed, or none at all. For that reason, functions are treated as nouns in 𝒌, and while in certain contexts verbs and functions can indeed be used interchangeably, functions differ from operators in the following fundamental way: 391 | 392 | * Operators can be applied using both infix and prefix notation 393 | * Functions can only be applied using prefix notation, also known as "functional form" 394 | 395 | This only sounds confusing until you see what this means in practice: 396 | 397 | ```q 398 | 399 | a:44;b:2 /a and b are typical nouns: variables holding an number 400 | 401 | a-b /a dyadic verb 'subtract', applied using infix notation 402 | 42 403 | 404 | -[a;b] /the same thing in prefix notation, aka functional form 405 | 42 406 | 407 | sub:{x-y} /a dyadic noun 'sub', a function assigned to a variable 408 | 409 | sub[a;b] /the only way to use 'sub' is to treat it as a function 410 | 42 411 | 412 | a sub b /putting 'sub' between its operands is not going to work 413 | a sub b 414 | ^ 415 | class error 416 | 417 | ``` 418 | 419 | We shall revisit parts of 𝒌 speech again, especially adverbs, and if this quick introduction left you puzzled, for now it is safe to think that verbs are simply "built-in operators". 420 | 421 | ------------------- 422 | 423 | **recap** 424 | 425 | So far you know how to: 426 | 427 | * add two integers 428 | * assign values to variables 429 | * declare and call basic functions 430 | * be friends with `x`, `y` and `z` 431 | * tell monadic and dyadic apart 432 | * explicitly declare monadic ops 433 | * deal with verbs and nouns 434 | * annotate your code 435 | 436 | This is a good start, but tells you absolutely nothing about what 𝒌 really is, and from here things will start to get real. 437 | 438 | ## numbers 439 | 440 | ### vectors vs atoms 441 | 442 | The word `atom` is a synonym for `scalar value`, or simply `scalar`. Every language has them, and in 𝒌 they are as useful as elsewhere. But 𝒌 belongs to a family of *vector languages*, which means its fundamental type is an ordered set of atoms or other ordered sets. 443 | 444 | In 𝒌 parlance, terms `array`, `list` and `vector` are often used interchangeably and refer to the same thing, but we will stick with `vector` to avoid confusing you, because vectors are much more general than classic *arrays* and have nothing to do with *linked lists*. 445 | 446 | ```q 447 | x:(0;1;2;3;4) /one way of declaring an integer vector 448 | y:0 1 2 3 4 /same effect using more informal syntax 449 | 450 | x 451 | 0 1 2 3 4 452 | y 453 | 0 1 2 3 4 454 | 455 | a:42 /a scalar variable, an atom with a name 456 | v:,42 /a vector of length 1, one integer item 457 | ``` 458 | 459 | k is strictly "**pass by value**", i.e. there are no references or pointers being passed around, although it is an illusion created by the underlying implementation. In reality, k avoids making copies of stuff unless it becomes absolutely necessary: 460 | 461 | ```q 462 | x:0 1 2 3 4 /everything is passed by value 463 | y:x /so this will make a copy of x 464 | y /y is a clone, not a reference 465 | 0 1 2 3 4 /(in reality it only pretends) 466 | 467 | y:x:0 1 2 3 4 /same effect in one expression 468 | /y and x are actually the same 469 | /until one of them is modified 470 | ``` 471 | 472 | 473 | The first thing you need to know about vectors is that most operations you expect to work for atoms work equally well for vectors, too: 474 | 475 | ```q 476 | x:y:0 1 2 3 4 /x and y are twin copies 477 | 478 | x+y /pairwise sum of vectors 479 | 0 2 4 6 8 480 | 481 | x*y /product is pairwise too 482 | 0 1 4 9 16 483 | 484 | x%y /division is %, get used 485 | ø 1 1 1 1 486 | 487 | 0%0 /ø is nan, void and null 488 | ø 489 | 490 | x=y /compare x to y pairwise 491 | 1 1 1 1 1 /1 is truthy, 0 is false 492 | ``` 493 | 494 | 495 | 496 | Mixing atomic and vector operands makes total sense and is very useful: 497 | 498 | ```q 499 | x:0 1 2 3 4 500 | 501 | x+1 /increment all elements 502 | 1 2 3 4 5 503 | 504 | x=1 /compare each of x to 1 505 | 0 1 0 0 0 506 | 507 | x%0 /divide each by 0, ouch 508 | ø ∞ ∞ ∞ ∞ /∀x∈ℚ (x%0) ∈ {-∞,∞,ø}, beware (0%0) = ø, enjoy responsibly 509 | 510 | 3%x /divide 3 by each of x 511 | ∞ 3 1.5 1 0.75 512 | ``` 513 | 514 | 515 | **Indexing** is zero-based as you would expect, and if you pass a vector of indices, you get back a vector of items: 516 | 517 | ```q 518 | x:2 4 8 16 32 519 | 520 | x[2] /get 3rd element of x 521 | 8 522 | 523 | x 2 /no need for brackets 524 | 8 525 | 526 | x[1 4] /get 2nd and 5th item 527 | 4 32 /gives another vector 528 | 529 | x 1 4 /no need for brackets 530 | 4 32 531 | 532 | y:1 4 /y is an index vector 533 | x y /same as x[y] less [] 534 | 4 32 535 | ``` 536 | 537 | 538 | Pairwise operations on vectors of incompatible **shape** make much less sense to 𝒌 than division by zero, and will throw an error: 539 | 540 | ```q 541 | x:0 1 2 3 4 542 | y:0 1 2 543 | x+y 544 | 545 | x+y 546 | ^ 547 | length error 548 | ``` 549 | 550 | Note the difference between shape and length. This reminds us that a vector can be composed not just from atoms but from other vectors as well, and there is no practical limit on the depth of nesting. In other words, vectors can have **arbitrary shape**: 551 | 552 | ```q 553 | y:(,1;1 1;1 2 1;1 3 3 1) /pascal's triangle 554 | y 555 | ,1 556 | 1 1 557 | 1 2 1 558 | 1 3 3 1 559 | ``` 560 | 561 | Vector arithmetic is **penetrating**, which means that vector operators *apply at depth* for as long as operands have compatible shape. It is a good time to introduce the `monadic +x` to better see how this works: 562 | 563 | ```q 564 | mat:(1 2 3;4 5 6;7 8 9) /shall there be mat: 565 | mat /a square matrix 3*3 566 | 1 2 3 567 | 4 5 6 568 | 7 8 9 569 | 570 | tam:+mat /monadic + is 'flip' 571 | tam /y is a transposed x 572 | 1 4 7 573 | 2 5 8 574 | 3 6 9 575 | 576 | mat=tam 577 | 1 0 0 578 | 0 1 0 579 | 0 0 1 580 | 581 | mat>tam 582 | 0 0 0 583 | 1 0 0 584 | 1 1 0 585 | 586 | mat+42 587 | 43 44 45 588 | 46 47 48 589 | 49 50 51 590 | ``` 591 | 592 | ----------------------- 593 | 594 | **practice** 595 | 596 | First, lets make sure `+x flip` operator transposes rectangular matrices just as well as squares, which would be of little surprise. Then try to flip something less obvious, and after that you have two more transformations to apply. Inspect all intermediate results and make sure you follow their logic: 597 | 598 | ```q 599 | mat:(1 2 3 4;6 7 8 9) /a rectangular matrix 600 | +mat /flip it, no big deal 601 | 1 6 602 | 2 7 603 | 3 8 604 | 4 9 605 | 606 | t:(,1;1 1;1 2 1;1 3 3 1) /t is triangle vector 607 | t:+t /flip it, what gives? 608 | t:t>0 /t greater than zero? 609 | t:(+t)+t /t transposed plus t? 610 | █ 611 | ``` 612 | 613 | ----------------------- 614 | 615 | No rocket science, all pretty basic, but carry on. 616 | 617 | ### types of types 618 | 619 | Type system in 𝒌 gets strict when it has to, but also agrees that implicit casts and type coercion have their strengths — especially when done right, which in 𝒌 they are. 620 | 621 | Before we see the examples, the first thing you need to know about types in 𝒌 is that they are divided into two broad classes: **vector types** and **atomic types**. That is, a vector with a single element, say, `42`, is not the same type as an atomic integer of the same value. Finally, since functions and other things in 𝒌 are also assignable values, they also have their place in the type system. Those are **special types** and we will not cover them here in much detail. 622 | 623 | Here is a quick overview of basic 𝒌 types and their symbolic names: 624 | 625 | ```q 626 | `i int 627 | `f float 628 | `j long 629 | `c char 630 | `n name 631 | `D date 632 | `t time 633 | `a dict 634 | `A table 635 | `. vect 636 | ``` 637 | 638 | This is not very revealing, so lets see them in action. The operator to query the type of anything in 𝒌 is `monadic @x`, and if you are not sure what we mean by `monadic`, perhaps it is a good time to start over. 639 | 640 | 641 | ```q 642 | @42 /int scalar 643 | `i 644 | 645 | @424242j /int64 atom 646 | `j 647 | 648 | @.5 /float atom 649 | `f 650 | 651 | @0 1 2 /int vector 652 | `i 653 | 654 | v:0 1 .5 2 655 | @v /0.5 promotes vector to float 656 | `f 657 | 658 | v 1 /2nd item, f is short for 1.0 659 | 1f 660 | ``` 661 | 662 | 663 | Like in C, there is no dedicated type for strings in 𝒌. Strings are just **char vectors**: 664 | 665 | ```q 666 | @"k" /"k" is char atom 667 | `c 668 | 669 | s:"kei";@s /s is char vector 670 | `c 671 | 672 | s 0 /1st element of s 673 | "k" 674 | ``` 675 | 676 | 677 | A type called **name** is the same idea as **internalized string** found in some other languages. This means that a single instance of an arbitrarily long string can be placed into a global hash table that persists for a lifetime of a 𝒌 process and can later be referenced by its hash key as many times as necessary without creating additional copies of the string. 678 | 679 | We could say that in case of names 𝒌 actually passes *references* instead of *values*, but they are not true pointers and there is no arithmetic defined for them. Names come handy in many situations, for now lets just see how they quack: 680 | 681 | ```q 682 | a:`kei /"kei" is now internalized 683 | @a /name atom is its hash key 684 | `n 685 | 686 | b:`kei`kei`kei /three references to "kei" 687 | @b /vector of string pointers 688 | `n 689 | 690 | @`"ken iverson" /spaces in names, why not? 691 | `n 692 | 693 | $a /$ converts names to chars 694 | "kei" 695 | ``` 696 | 697 | 698 | **Temporal types** in k are `date` and `time`: 699 | 700 | ```q 701 | d:1981-02-01 /yyyy-mm-dd, ok to expect iso 8601 compliance 702 | @d /NOTE: date atom is `D same as date vector `D 703 | `D 704 | 705 | t:12:34:56.789 /hh:mm:ss.sss, max resolution is milliseconds 706 | @t 707 | `t 708 | 709 | dt:d+t;dt /advanced date ops is a story for another day 710 | 1981-02-01T12:34:56.789 711 | ``` 712 | 713 | 714 | **Dictionaries** are maps of keys to values, also known as *hashmaps* or, more generally, *associative arrays*. They are as useful in 𝒌 as elsewhere, but unlike many languages where this data structure is built in, 𝒌 allows _both_ keys and values to be of *any* type, both vector and scalar. This might sound a bit confusing at first — since nothing prevents you from, say, constructing a dictionary where keys are themselves dictionaries — but in able hands this oddity becomes a very powerful tool. 715 | 716 | Dictionaries have the type **\`a**, and the notation for defining them uses a dyadic operator `! key`: 717 | 718 | ```q 719 | d:`a`b!(1 2 3;4 5 6) /keys!values 720 | d 721 | a|1 2 3 722 | b|4 5 6 723 | 724 | @d /dict type is `a 725 | `a 726 | 727 | d`a /key lookup (aka d[`a] or d `a) 728 | 1 2 3 729 | 730 | !d /dict keys 731 | `a`b 732 | 733 | . d /dict vals (note the space!) 734 | 1 2 3 735 | 4 5 6 736 | ``` 737 | 738 | 739 | **Tables** are *flipped dictionaries*, and they require a separate large discussion. Here, we will only describe their syntax for the sake of completeness. Table type is **\`A**, and notation is the same as dict, only with `+x flip` operator prepended. As common sense implies, a dictionary won't flip unless all values are of the same length. 740 | 741 | No comments on any of this for now — but if you can follow the logic of what is going on here, you'll agree that in some rare cuircumstances a technology is indeed indistinguishable from magic. See for yourself: 742 | 743 | ```q 744 | x:`goo`apl`amz 745 | y:1 2 3 4 5 6 746 | 747 | t:+`s`d`p!(x,x;`D$y;1.5*y) /table is a transposed dict 748 | @t 749 | `A 750 | 751 | t /trades: stock, date, price 752 | s d p 753 | --- ---------- --- 754 | goo 2024-01-02 1.5 755 | apl 2024-01-03 3 756 | amz 2024-01-04 4.5 757 | goo 2024-01-05 6 758 | apl 2024-01-06 7.5 759 | amz 2024-01-07 9 760 | 761 | select avg p by s from t /pretend you didn't see this 762 | s |p 763 | ---|---- 764 | amz|6.75 765 | apl|5.25 766 | goo|3.75 767 | ``` 768 | 769 | 770 | **Lambdas** are assignable values and have their own type, and more than one: 771 | 772 | ```q 773 | @{x+y} /type of lambda gives away its rank 774 | `2 775 | 776 | nil:{0} /don't expect nil to have rank zero 777 | @nil /niladic functions don't exist in k 778 | `1 779 | 780 | a:{x},{x+x},{x*x} /a vector of lambdas, sure, why not 781 | a[2]16 /calls 3rd lambda, same as a[2][16] 782 | 256 783 | ``` 784 | 785 | 786 | **Null values** in 𝒌 are typed, integer null is `Ø` and float null is `ø`. **Infinity** is a scalar float `∞`. Working with nulls and infinities can be very tricky, and it is very important to pay attention to their types: 787 | 788 | ```q 789 | n:ø /float null is type float 790 | @n 791 | `f 792 | 793 | N:Ø /int null is type integer 794 | @N 795 | `i 796 | 797 | n=N /equal, but not the same! 798 | 1 799 | 800 | @∞ /infinity is a float atom 801 | `f 802 | ``` 803 | 804 | It is evident that nulls and infinities are *Unicode glyphs*. Although it is very easy to set up keyboard shortcuts for them, there are idiomatic ways to enter them using plain ASCII (but avoid if you can): 805 | 806 | ```q 807 | 0%0 /float null is zero div by zero 808 | ø 809 | 810 | 42e /missing exponent is float null 811 | ø 812 | 813 | `i$0%0 /int null is rounded float null 814 | Ø 815 | 816 | _0e /int null via `monadic _x floor' 817 | Ø 818 | 819 | 1%0 /inf is just reciprocal of zero 820 | ∞ 821 | %0 /inf via `%x 'inverse` operator 822 | ∞ 823 | 824 | PI:3.14159265358979323846264338327950288 825 | π=PI /synonym of π in ascii, but why 826 | 1 827 | ``` 828 | 829 | 830 | **Composite vector** type, or you could also say **mix vector**, is of special mention. Such vectors are either a mixture of atoms of disparate types, or contain something more complex than atoms, e.g. other vectors: 831 | 832 | ```q 833 | c:0,1,"a",2,3 /a char impostor among ints, c is mix 834 | @c /type of a mixed list is backtick-dot 835 | `. 836 | 837 | x:(1 2 3;4 5 6;7 8 9) /a vector of vectors is composite too 838 | x 839 | 1 2 3 840 | 4 5 6 841 | 7 8 9 842 | @x 843 | `. 844 | ``` 845 | 846 | 847 | **Type casting**, both explicit and implicit, is demonstrated by the following examples which also give a general feel of how type coercion behaves. The `cast` operator in k is a dyadic `t$x`, where `t` is a type name and `x` is a subject of cast: 848 | 849 | ```q 850 | 1+.5 /int plus float is float, no surprises here 851 | 1.5 852 | 853 | 1f*2 /any float operand promotes result to float 854 | 2f 855 | 856 | `i$42.99 /explicit cast from `f to `i drops mantissa 857 | 42 858 | 859 | `i$42.0 42.99 /`f to `i will round down the entire vector 860 | 42 42 861 | 862 | 0+"abc" /integer operand demotes `c vector to ascii 863 | 97 98 99 864 | 865 | `c$1+"HAL9000" /add 1 to ascii, cast back to `c, surprise: 866 | "IBM:111" 867 | 868 | "012"+"345" /sum ascii codes of chars, result stays `c 869 | "ceg" 870 | 871 | a:1 2 3 /lets start with a nice uniform int vector 872 | a[0]:1f /now, replace its 1st element with a float 873 | @a /whoops, our int vector got demoted to mix 874 | `. 875 | a:`f$a;@a /explicit cast to float solves the problem 876 | `f 877 | 878 | `i$1981-02-01 /dates represented as ints look like this: 879 | -15674 880 | 881 | 15674+1981-02-01 /and they are simply offsets in days from: 882 | 2024-01-01 883 | 884 | `i$2024-01-01 /so the epoch date itself must be exactly: 885 | 0 886 | 887 | 1+`kei /no math for names, this type is immutable 888 | ^ 889 | type error 890 | 891 | @@42 /what is type name of a type name of int? 892 | █ 893 | ``` 894 | 895 | There are things left to be said about the type system, but the expression `@@42` above (which evaluates to some kind of wordplay, *type name of a type name is `name`*) urges us to the next section which is all about how to make sense of this expression. 896 | 897 | ------------------ 898 | 899 | ### right to left 900 | 901 | As you must have noticed, the syntax for indexing vectors and calling functions is identical: 902 | 903 | ```q 904 | l:{x+x} /some monadic function l[x] 905 | t:2 4 8 16 /some random integer vector 906 | r:0 3 /index vector: indices of t 907 | 908 | l[t] /apply function l to each t 909 | 4 8 16 32 910 | 911 | t[r] /items of t at indexes in r 912 | 2 16 913 | 914 | l[t[r]] /compose: apply l to t at r 915 | 4 32 916 | ``` 917 | 918 | What we also know that 𝒌 actively encourages us to omit brackets whenever possible, so lets do exactly that: 919 | 920 | ```q 921 | l t r /exactly the same as l[t[r]] 922 | 4 32 923 | ``` 924 | 925 | And here is comes: once we drop the brackets, it suddenly becomes absolutely natural to read this expression *right to left*. Take your time to contemplate and absorb this fact. In very little time you will see how it works in practice, and once you put it to practice yourself, you will agree that this way of functional composition is simple, elegant and intuitive: 926 | 927 | **k expressions are read, written and evaluated right to left.** 928 | 929 | But when we say "expressions" we don't mean "programs", and this is a very important distinction: 930 | 931 | **k programs are read, written and evaluated left to right.** 932 | 933 | This might sound confusing, but look at the diagram of a small 𝒌 **program** that consists of three identical expressions `l t r`, with parens added for clarity. Further down is the order of evaluation of the entire program, which leaves no room for confusion: 934 | 935 | ```q 936 | / L T R 937 | /(l t r);(l t r);(l t r) 938 | 939 | / I > II > III 940 | /(3 2 1);(6 5 4);(9 8 7) 941 | ``` 942 | 943 | And now that we know the way the rivers flow in 𝒌 land, we are equipped to discuss the next subject. 944 | 945 | #### precedence 946 | 947 | Very early on in our lives, we are taught there is a good reason for multiplication and division to bind stronger than addition and subtraction, so they must be computed first. Later on, we are taught that most computer languages require us to learn a lot more complex systems of operator precedence to do anything useful with them to begin with, and much later on — all the deadly caveats hidden in those systems. But in 𝒌, the question of operator precedence is fully answered by their order of evaluation, so buckle up. Here is the one and only rule ever to be learned about precedence in 𝒌: 948 | 949 | **There is no operator precedence in 𝒌, unless it is explicitly defined by round brackets.** 950 | 951 | By default, **all operators** in a 𝒌 expression are treated equally and evaluated strictly from **right to left**. Obviously, this includes **arithmetic** operators as well, and before you ask yourself how `*` can possibly have no precedence over `+`, consder a few basic math expressions annotated with their order of evaluation: 952 | 953 | ```q 954 | 3+2+1 /"take 1, add 2, add 3" 955 | 6 956 | 957 | 3*2+1 /"take 1, add 2, multiply by 3" 958 | 9 959 | 960 | (3*2)-1 /"take 2, multiply by 3, sub 1" 961 | 5 962 | 963 | -1+3*2 /same thing, without overrides 964 | 5 965 | ``` 966 | 967 | As you see, it is much easier to get used to the lack of precedence than it appears at first — indeed, simplicity is the worst enemy of complexity. The last two examples demonstrate the basic strategy of avoiding parens entirely, and there is a good reason for ditching them — it makes the order of evaluation completely **linear**. 968 | 969 | Although precedence overrides are sometimes inevitable and can be beneficial, they have an adverse effect on **readability**. Basically, while reading a 𝒌 expression, what you generally want is to go fast and uninterrupted — and precedence overrides interrupt the natural flow of comprehension. Good 𝒌 programmers think of others before themseves, and seek to produce code which follows the natural order of evaluation by minimizing the use of round brackets. 970 | 971 | ---------------- 972 | 973 | Now we can revisit the last expression from the previous chapter: 974 | 975 | ```q 976 | @@42 /"type name of a type name of 42" actually reads backwards: 977 | `n /"get 42, apply monadic @, get `i, apply monadic @, get `n" 978 | ``` 979 | 980 | A convincing proof that type name of a type name in 𝒌 is indeed a `name`.∎ 981 | 982 | ---------------- 983 | 984 | **practice** 985 | 986 | Let's revisit the code from the first snippet in this document: 987 | 988 | ```q 989 | x:(1 2 3;4 5 6;7 8 9) 990 | 991 | x=x+1 /how can a universal truth be so false? 992 | █ 993 | ``` 994 | 995 | Too easy, but we'll make up for it. 996 | 997 | ---------------- 998 | 999 | ### no stinking loops 1000 | 1001 | This part might be easier to digest than the previous, especially if you are familiar with functional programming. The title, borrowed without permission from [the legendary k resource](http://nsl.com), says it all - you will not find a 𝒌 construct that resembles an explicit `for` loop, and although there is a `while` construct in 𝒌, it is almost never used in practice. 1002 | 1003 | And this is not just to avoid untold damages from trivial errors people keep making in their loop definitions. The main reason explicit loops are banned from 𝒌 is because it offers something better. The idea that displaces them is a simple and strong abstraction called *adverbs*, but before we see them in action, it helps to understand why they are called that way: 1004 | 1005 | An **adverb** is a **modifier** that accepts a user-defined function or a native operator and returns a new monadic or dyadic **verb** which acts on one or two **input operands** in some desirable way to produce an **output**. Input and output can be scalar values or vectors, depending on the adverb and the action it modifies. 1006 | 1007 | The formal definition sounds a bit dry, so let's consider a classic example of how adverbs replace loops: 1008 | 1009 | **"Compute a sum of elements of a given integer vector."** 1010 | 1011 | Thinking in implicit loops suggests something we've all done a million times: 1012 | 1013 | ```c 1014 | int sum(int[]in){ 1015 | int i=0,acc=0; 1016 | for(;i 1059 | ---------------- 1060 | adverb **over** is `f/x` 1061 | 1062 | adverb **scan** is `f\x` 1063 | 1064 | where `f` is a `dyadic` verb and `x` is an input vector 1065 | 1066 | ```q 1067 | a:0 1 2 3 4 /some data 1068 | 1069 | +/a /puts a + between items, 0+1+2+3+4 1070 | 10 /and returns the final result only 1071 | 1072 | +\a /scan returns intermediate results 1073 | 0 1 3 6 10 /running sum, aka debugger of over 1074 | ``` 1075 | 1076 | 1077 | ---------------- 1078 | adverb **each** is `f'x` 1079 | 1080 | where `f` is a `monadic` verb and `x` is an input vector 1081 | 1082 | ```q 1083 | d:42,"a",`kei /some composite vector 1084 | fn:{@x} /some monadic function 1085 | fn'd /apply fn to each of d 1086 | `i`c`n /the type of each item 1087 | 1088 | f:{1%x},{x*x} /reciprocal and square 1089 | {x 25}'f /call each of f for 25 1090 | 0.04 625 1091 | ``` 1092 | 1093 | 1094 | ---------------- 1095 | 1096 | adverb **eachleft** is `x f\:y` 1097 | 1098 | adverb **eachright** is `x f/:y` 1099 | 1100 | where `f` is a `dyadic` verb and `x` and `y` are left and right inputs, 1101 | either vectors or atoms 1102 | 1103 | ```q 1104 | 10 20 30-\:5 /eachleft does (10-5),(20-5),(30-5) 1105 | 5 15 25 /each of left, substracted by right 1106 | 1107 | 5-/:0 20 30 /eachright does (5-0),(5-20),(5-30) 1108 | 5 -15 -25 /left, substracted by each of right 1109 | ``` 1110 | 1111 | 1112 | ---------------- 1113 | 1114 | adverb **eachprior** is `x f':y` and `(f':)x` 1115 | 1116 | first form is `seeded eachprior` where `f` is a `dyadic` verb, and `x` is a 1117 | seed value and `y` is an input vector 1118 | 1119 | second form is `seedless eachprior` where `f` is a `dyadic` verb, and `x` is 1120 | an input vector 1121 | 1122 | 1123 | ```q 1124 | 2+':4 8 16 /seeded eachprior gives (2+4),(4+8),(8+16) 1125 | 6 12 24 /sum 1st item and seed, then sum each item and its prior 1126 | 1127 | (+':)4 8 16 /seedless eachprior gives (4),(4+8),(8+16) 1128 | 4 12 24 /first item stays as is, then sum each item and its prior 1129 | ``` 1130 | ---------------- 1131 | 1132 | This doesn't seem like much, adverbs seem to be doing pretty basic stuff. But hold that thought for a minute. 1133 | 1134 | **recap** 1135 | 1136 | We have seen: 1137 | 1138 | * what 𝒌 type system looks like 1139 | * how basic vector and atom math works 1140 | * which way to read and comprehend 𝒌 code 1141 | * what is the only existing precedence rule 1142 | * why there is no `for` and why there are `adverbs` 1143 | 1144 | ---------------- 1145 | 1146 | **practice** 1147 | 1148 | Consider a slightly less trivial example of `eachleft` working together with `x*y multiply` on two vector operands: 1149 | 1150 | ```q 1151 | x:!9 /a vector of integers from 0 til 8 1152 | x+:1;x /add 1 to each of x, same as x:x+1 1153 | 1 2 3 4 5 6 7 8 9 1154 | 1155 | x*\:x /what would be x times eachleft x? 1156 | █ 1157 | ``` 1158 | ---------------- 1159 | 1160 | Bonus questions: 1161 | 1162 | ```q 1163 | kcc:+/∞ /how come k sums up to infinity this fast? 1164 | kcc 1165 | █ 1166 | 1167 | +\(1+!42)%0 /how about a running sum of 42 infinities? 1168 | █ 1169 | ``` 1170 | 1171 | Make sure you can follow the logic of these examples before advancing to the next chapter, where things will get a lot less innocent. But if you didn't find the exercise challenging enough, here is another one: 1172 | 1173 | ---------------- 1174 | 1175 | Extra bonus: 1176 | 1177 | ```q 1178 | q:7;GF:!q;q\GF*\:GF /what does GF stand for? 1179 | █ 1180 | 1181 | /`c$97+2/:+2\:32 458 1172 1443 275 1182 | ``` 1183 | 1184 | ## proverbs 1185 | 1186 | ### how to solve it 1187 | 1188 | The title of this chapter is borrowed from a legendary book published in 1945, a small volume by mathematician George Pólya where he shows how to tackle problems and arrive to solutions. It is a very inspiring read. 1189 | 1190 | Let's tackle a little problem. We will look at a 𝒌 function that actually does something very useful and implements a familiar algorithm. The subject of the game is to figure out how it is implemented in 𝒌 and to identify the algorithm. It is very useful to dissect all of it on paper, so put your interpreter aside for now. 1191 | 1192 | So, here is the code: 1193 | 1194 | ```q 1195 | /what is f[], and can we read it? 1196 | f:{$[2>#?x;x;,/f'x@&:'~:\x<*1?x]} 1197 | ``` 1198 | 1199 | This little monster is deliberately designed to make as little sense as possible at first glance, but once we take it apart, we hope you'll agree it is actually very simple and readable: 1200 | 1201 | ```q 1202 | 1203 | f:{$[2>#?x;x;,/f'x@&:'~:\x<*1?x]} 1204 | 1205 | f:{...} /f is a function, that is a good start 1206 | f:{.x.} /f takes only one implicit argument, x 1207 | f:{.f.} /f clearly calls f, so it is recursive 1208 | 1209 | $[?;?;?] /the entire body of f is nothing else but 1210 | $[c;t;f] /a ctf cond, aka if-then-else aka ternary 1211 | 1212 | 2>#?x /c: boolean condition 1213 | x /t: do this if c is 1 1214 | ,/f'x@&:'~:\x<*1?x /f: do that if c is 0 1215 | 1216 | 2>#?x /we don't how to read this, but it is clear that f[] 1217 | /halts recursion when it evaluates to 1, returning x 1218 | /so lets find out what it means, going right to left 1219 | 1220 | /we have two new operators, both in monadic context: 1221 | /monadic ?x is 'distinct' unique elements of x 1222 | /monadic #x is 'count' count the elements of x 1223 | 1224 | #?x /'count distinct' count unique elements of x 1225 | 2>#?x /'greater' true if count distinct x is less than 2 1226 | 1227 | $[2>#?x;x;...] /"if x has <2 unique items, return x, otherwise ..." 1228 | ``` 1229 | 1230 | Coffee break, here is what we know so far: 1231 | 1232 | 1. the function is recursive 1233 | 1. its overall control flow 1234 | 2. the condition that stops recursion 1235 | 1236 | This gives us confidence to wrestle down the last part, the recursion step: 1237 | 1238 | ```q 1239 | 1240 | ,/f'x@&:'~:\x<*1?x /this must be the recursion step, read right to left: 1241 | 1242 | x:4 0 1 2 /a small dataset to help us see what is going on here 1243 | 1244 | rnd:1?x /x?y is 'find': picks x random elements from vector y 1245 | rnd /list with one random item from x 1246 | ,2 1247 | 1248 | rnd:*rnd;rnd /*x is 'first': return the head element of a vector x 1249 | 2 1250 | 1251 | cmp:x= rnd" 1276 | 1277 | x:f'pts /adverb each: apply f to each partition, recurse down 1278 | 1279 | ,/x /,/ is 'raze': unnest aka flatten a vector of vectors 1280 | ,/(1 2 3;4 5 6) /in other words, raze flattens first level of nesting 1281 | 1 2 3 4 5 6 1282 | ``` 1283 | 1284 | Note that it is the first time we have seen [rank override](#explicit-monadics) in action. In both cases, explicit monadics are passed to an adverb — and this is a very typical use case. 1285 | 1286 | Now that we know what every specific part does, we can zoom out and see the big picture. Feel free to use the interpreter to play around and test your ideas. 1287 | 1288 | ------------------- 1289 | 1290 | And of course, `f` is nothing else but: 1291 | 1292 | ```q 1293 | qs:{$[2>#?x;x;,/qs'x@&:'~:\x<*1?x]} /qsort on rand pivot 1294 | 1295 | i:9 2 5 5 1 8 1 3 6 1 /a hairy int shuffle 1296 | f:2.6 -∞ 8.6 π 1.7 ∞ 3.5 5.6 /a π in a float soup 1297 | c:"edrofgtnljgrpliifp" /a char entropy pool 1298 | mess:(i;f;c) 1299 | 1300 | qs'mess /restore order quick 1301 | █ 1302 | ``` 1303 | 1304 | And of course this is not the quickest `quicksort` ever written, but this is just a toy. In real life you would simply use the built-in operator which is a lot more efficient: 1305 | 1306 | ```q 1307 | ^3 2 1 /monadic ^x is 'sort' 1308 | 1 2 3 1309 | 1310 | sort:^:' /explicit 'sort each' 1311 | 1312 | (qs'mess)~(sort mess) /x~y 'match' operands 1313 | 1 1314 | 1315 | \t:10000 qs'mess /apply qs 10000 times 1316 | █ 1317 | \t:10000 sort mess /fix 10000x more mess 1318 | █ 1319 | ``` 1320 | 1321 | As you see, native sort is incomparably faster. But what our DIY sort function is very good for is to demonstrate the principle of **doing more with less**, and that is what 𝒌 is all about. 1322 | 1323 | Check out examples of `quicksort` in the wild in [C++](https://gist.github.com/christophewang/ad056af4b3ab0ceebacf), [Python](https://gist.github.com/anirudhjayaraman/897ca0d97a249180a48b50d62c87f239), [JavaScript](https://gist.github.com/claudiahdz/39a86084edaaabe7fc17c321c0bb6896) and [Java](https://github.com/Code2Bits/Algorithms-in-Java/blob/master/sort/src/main/java/com/code2bits/algorithm/sort/QuickSort.java). 1324 | 1325 | ------------------- 1326 | 1327 | **recap** 1328 | 1329 | Previously we have seen: 1330 | 1331 | * monadic `!x til (first x integers)` 1332 | * monadic `%x inverse (reciprocal)` 1333 | * monadic `+x flip (transpose)` 1334 | * monadic `$x string` 1335 | * monadic `@x type` 1336 | * monadic `^x sort` 1337 | * dyadic `t@x cast` 1338 | * dyadic `x:y assign` 1339 | * dyadic `x=y equal` 1340 | 1341 | And `qs` code brought a few more: 1342 | 1343 | * ctf cond `$[c;t;f]` 1344 | * monadic `?x distinct` 1345 | * monadic `#x count` 1346 | * monadic `*x first 1347 | * monadic `~x not` 1348 | * monadic `&x where` 1349 | * monadic `,/x raze` 1350 | * dyadic `x@y index` 1351 | * dyadic `x~y match` 1352 | * dyadic `x?y find` 1353 | 1354 | Finally, you are now equipped with the most ubiquitous system routine: 1355 | 1356 | * `\t:n expr` benchpress an expression `n` times, result is in milliseconds 1357 | 1358 | Although this is still a small part of 𝒌 operator arsenal, if you can do `quicksort` with this much, you can do a lot more. And then add vector arithmetic, and then take everything to the power of six adverbs. 1359 | 1360 | ------------------- 1361 | 1362 | **practice** 1363 | 1364 | 1. take another good look at the code of `qs` function 1365 | 2. retrace the steps of the code analysis 1366 | 3. in a new 𝒌 session, reproduce `qs` from scratch 1367 | 1368 | It sounds much harder than it really is. It might take more than one attempt, but you will be amazed how fast you will get there. However, before advancing to the next chapter, make sure that you do. 1369 | 1370 | ------------------- 1371 | 1372 | Among other things, the annotated breakdown of `qs` code gives a good impression of what is typically going on inside of 𝒌 programmer's head — but tells you nothing about how _fast_ it usually happens. A proficient 𝒌 programmer would read and understand `qs` in well under two minutes. With a bit more practice, you will agree that reading 𝒌 programs is easy and fun. 1373 | 1374 | ------------------- 1375 | 1376 | ### three triangles 1377 | 1378 | It is time to write our first 𝒌 program, and this time around there will be a lot less hand-holding. We will solve the classic Project Euler [p18](https://projecteuler.net/problem=18), also known as [p67](https://projecteuler.net/problem=67): 1379 | 1380 | ```q 1381 | By starting at the top of the triangle below and 1382 | moving to adjacent numbers on the row below, the 1383 | maximum total from top to bottom is 23: 1384 | 1385 | →3 1386 | ↑7 4 1387 | 2 ↑4 6 1388 | 8 5 ↑9 3 1389 | ------------------ 1390 | 9 + 4 + 7 + 3 = 23 1391 | ``` 1392 | Problems 18 and 67 are simply two bigger triangles, and the challenge is to find the **sum** of maximum paths in them. While 18 can be solved by bruteforce, 67 can not, but the efficient algorithm is absolutely trivial. It is given away in the example above: we simply need to fold rows going bottom up, like so: 1393 | 1394 | ```q 1395 | 8 5 9 3 1396 | 8 9 9 /max 1397 | + + + /sum 1398 | 2 4 6 1399 | 10 13 15 /out 1400 | 13 15 /max 1401 | + + /sum 1402 | 7 4 1403 | 20 19 /out 1404 | 20 /max 1405 | + /sum 1406 | 3 1407 | 23 /out 1408 | ``` 1409 | 1410 | It is easy to see that the key to the solution is a function that *reduces* the current row (`max`) and *merges* it into the next (`sum`). It expects two arguments, i.e. both rows to work with, and returns `out`. That's all we need to implement it: 1411 | 1412 | ```q 1413 | r4:8 5 9 3 /take two bottom rows to assist thinking 1414 | r3:2 4 6 1415 | 1416 | 0|42 /x|y is max: returns largest of operands 1417 | 42 1418 | 1_1 2 3 /x_y is drop: discards x items of a list 1419 | 2 3 1420 | 1421 | |':r4 /max eachprior: get max element pairwise 1422 | 8 8 9 9 1423 | 1424 | 1_|':r4 /drop first result of seedless eachprior 1425 | 1426 | r3+1_|':r4 /pairwise sum: merge bottom row into top 1427 | 10 13 15 1428 | 1429 | row:{y+1_|':x} /row reduction operation, x bottom, y up 1430 | 1431 | row[r4;r3] /r4 and r3 are fine, so should be others 1432 | 10 13 15 1433 | ``` 1434 | 1435 | Great, we have the reduction function, now let's apply it *over* the test triangle to make sure it folds it into what we expect. Since we are reducing a vector into an atom, it is abundantly clear which adverb we want to use: 1436 | 1437 | ```q 1438 | t:(,3;7 4;2 4 6;8 5 9 3) 1439 | 1440 | |t /monadic |x is reverse the order of vector 1441 | 8 5 9 3 1442 | 2 4 6 1443 | 7 4 1444 | ,3 1445 | 1446 | row/|t /apply row reductor over reversed triangle 1447 | ,23 1448 | 1449 | *row/|t /*x is first: simply return the first item 1450 | 23 1451 | 1452 | mxpath:{*{y+1_|':x}/|x} /maximum path in triangle vector 1453 | mxpath t 1454 | 23 1455 | ``` 1456 | 1457 | Looks like the `mxpath` is doing pretty well. Let's fetch the input file for the problem 67, load it, parse it and solve it: 1458 | 1459 | ```q 1460 | /backslash cmd executes an os command directly from k: 1461 | \curl https://projecteuler.net/project/resources/p067_triangle.txt > p67.txt 1462 | 1463 | lines:0:"p67.txt" /0:x reads a text file as vector of lines 1464 | lines 2 /just to make sure we have something real 1465 | "52 40 09" 1466 | 1467 | t:`k?'lines /parse each line of input as k expression 1468 | t 2 1469 | 52 40 9 1470 | 1471 | #t /a bigger triangle, but not a bigger deal 1472 | 100 1473 | 1474 | mxpath t /returns max path sum, the solution to 67 1475 | █ 1476 | ``` 1477 | 1478 | We didn't tell you do this, but the complete program can also be written as a single 𝒌 expression: 1479 | 1480 | ```q 1481 | *{y+1_|':x}/|`k?'0:"p67.txt" /load, parse and fold 1482 | █ 1483 | ``` 1484 | 1485 | ---------------------- 1486 | 1487 | **recap** 1488 | 1489 | We have seen some new stuff: 1490 | 1491 | * shell `\cmd` 1492 | * dyadic `x|y max` 1493 | * dyadic `x_y drop` 1494 | * monadic `|x reverse` 1495 | * monadic `0:x load text` 1496 | 1497 | ---------------------- 1498 | 1499 | **practice** 1500 | 1501 | 1. Reproduce `mxpath` from scratch in a new 𝒌 session, same way you did with `qs`. 1502 | 1503 | 2. Find a way to load and parse the triangle from problem 18, and solve it using your own code. 1504 | 1505 | 3. Verify solutions for 18 and 67 on [Project Euler](https://projecteuler.net). 1506 | 1507 | 4. As you know, once you provide a correct answer to an Euler problem, you can browse its discussion forum. You might want to check out some other solutions to 18 and 67 in other computer languages. 1508 | 1509 | 5. Measure `\t:n` for 100, 1000, 10000 runs and estimate the time complexity of the algorithm. Make sure disk I/O is excluded from the measurement. 1510 | 1511 | ------------------- 1512 | 1513 | ### apples and oranges 1514 | 1515 | There is no new material in this small chapter, so we can go straight to practice. 1516 | 1517 | **practice** 1518 | 1519 | Many things in life can only be understood in comparison. Compare functionality of these two programs: 1520 | 1521 | ```java 1522 | package com.less.with.more.doing.sort; 1523 | public final class qs{public void s(int[] x){}} 1524 | ``` 1525 | 1526 | ```q 1527 | qs:{$[2>#?x;x;,/qs'x@&:'~:\x<*1?x]} 1528 | ``` 1529 | 1530 | Now, compare the source code of these two: 1531 | 1532 | ```java 1533 | import java.util.Arrays; 1534 | public class S{public static void main(String[] a){ 1535 | int[] x={5,4,3,2,1};Arrays.sort(x); 1536 | System.out.printf("%s",Arrays.toString(x));}} 1537 | ``` 1538 | 1539 | ```bash 1540 | $ echo "^5 4 3 2 1" | k 1541 | 1 2 3 4 5 1542 | ``` 1543 | 1544 | Finally, compare the size of their runtimes: 1545 | 1546 | ```sh 1547 | 252M May 17 13:59 jdk-8u211-macosx-x64.dmg 1548 | 79M May 17 13:50 jre-8u211-macosx-x64.dmg 1549 | 109K May 17 13:54 k.tgz 1550 | ``` 1551 | 1552 | ---------------------- 1553 | 1554 | ### gladly beyond 1555 | 1556 | We have covered a lot of ground, good time to put things into perspective. Below is a complete map of 𝒌 operators, and those marked with bullets you have already seen and used at least once: 1557 | 1558 | ``` 1559 | x+y +x 1560 | ------------------------- 1561 | : ● assign 1562 | + ● add ● flip 1563 | - ● subtract ● negate 1564 | * ● multiply ● first 1565 | % ● divideby ● inverse 1566 | & ◦ min|and ● where 1567 | | ● max|or ● reverse 1568 | < ● less ◦ up 1569 | > ● more ◦ down 1570 | = ● equal ◦ group 1571 | ~ ● match ● not 1572 | ! ● key ● enum 1573 | , ● catenate ● list 1574 | ^ ◦ except ● sort 1575 | # ◦ take ● count 1576 | _ ● drop ● floor 1577 | ? ● find ● distinct 1578 | @ ● index ● type 1579 | . ◦ apply ● value 1580 | $ ● pad|cast ● string 1581 | ``` 1582 | 1583 | It really feels like we have explored more than we didn't, and it is huge progress indeed. But many things remain to be discovered, because operators is only one aspect of 𝒌 — and this short introduction could not possibly cover everything. 1584 | 1585 | We conclude with a list of subjects that you are now ready to explore on your own: 1586 | 1587 | |𝒌 language |𝒌 platform | 1588 | |:--------------------------------|:-----------------------------------| 1589 | |additional 𝒌 operators |debugging and securing 𝒌 systems | 1590 | |tables and k-sql language |building clients and servers in 𝒌 | 1591 | |vector aggregates |benchmarking, testing and tracing | 1592 | |entropy sources, math primitives |disk i/o, persistence and streaming | 1593 | |advanced use of adverbs, threads |ipc and distributed workloads | 1594 | |native csv, tsv, json and utf |fault tolerance and monitoring | 1595 | |integrated cryptography core |scripting, deployment, os tuning | 1596 | |nanosecond time, datetime math |interop with python, julia and c | 1597 | |𝒌-expressions, \`0 |tech support and user community | 1598 | |design of internal components |𝒌 resources, tools and packages | 1599 | 1600 | ----------------- 1601 | 1602 | Although ee cummings opened his famous poem with words *somewhere i have never travelled*, it seems that some 𝒌 programmers prefer to read poetry backwards. That explains a lot about the title of our final chapter. 1603 | 1604 | ![speaking of poetry](https://github.com/kparc/kcc/blob/master/img/kei.png?raw=true "speaking of poetry") 1605 | 1606 | ----------------- 1607 | 1608 | 1609 | 1610 | dream a little bigger [∎](mailto:me@kel.as) 1611 | --------------------------------------------------------------------------------