gk v1 is a full rewrite with ~10x performance improvement. There are some new features and a few NUCs to be aware of.
If a function returns a locally defined function, it is returned as a closure.
f:{i:0;{i+:1;i}}
g:f`
h:f`
g`
1
g`
2
h`
1
h`
2
If a function returns a dictionary of all functions, any of the functions in the returned dictionary that are locally defined will share a closure.
f:{i:j:0;r.inci:{(i+:1;j)};r.incj:{(i;j+:1)};r}
g:f`
g.inci`
1 0
g.inci`
2 0
g.incj`
2 1
g.incj`
2 2
Closures enable OOP patterns in gk (encapsulation, inheritance, and polymorphism).
/ base class
pet:{[name;species]
name0:name
species0:species
r.getname:{name0}
r.getspecies:{species0}
r}
/ derived class: dog (inherits from pet)
dog:{[name;breed]
r:pet[name;`dog]
breed0:breed
r.getbreed:{breed0}
r}
/ derived class: cat (inherits from pet)
cat:{[name;breed]
r:pet[name;`cat]
breed0:breed
r.getbreed:{breed0}
r}
/ creating objects
mydog:dog["Buddy";"Golden Retriever"]
mycat:cat["Whiskers";"Tabby"]
/ encapsulation - private variables are protected
@[{mydog.name0};`;:]
(1;"value")
mydog.getname` / "Buddy" - public accessor works
"Buddy"
/ polymorphism
petinfo:{(x.getname`;x.getspecies`)}
petinfo mydog
("Buddy";`dog)
petinfo mycat
("Whiskers";`cat)
v1 adds three builtins for matrix decomposition: lu, qr, and svd.
a:(1 2;3 4)
lu a
((0 1.0
1 0.0)
(1 0.0
0.3333333 1)
(3 4.0
0 0.6666667))
qr a
((0.3162278 0.9486833
0.9486833 -0.3162278)
(3.162278 4.427189
0 0.6324555))
svd a
((0.4045536 0.9145143
0.9145143 -0.4045536)
(5.464986 0
0 0.3659662)
(0.5760484 0.8174156
-0.8174156 0.5760484))
mul/lu a
(1 2.0
3 4.0)
mul/qr a
(1 2.0
3 4.0)
mul/svd a
(1 2.0
3 4.0)
There are several non-upward compatible changes in v1.
Binary k data format is incompatible between v0 and v1. Any files written out from v0 will not be readable in v1 and vice versa.
If the return value of a function is an assignment, the assigned value is returned as a quiet value.
This is consistent with how assignments work in open code, where variable assignments don't print anything to console, but do yield the assigned value:
a:1
q:a:1
q
1
Functions that return assignments now behave the same way:
f:{a:1}
f`
q:f`
q
1
List statements are evaluated left-to-right now, just like any other list of statements separated by semicolons.
(a:0;a+1)
0 1
{x,y}[x:0;x+1]
0 1
A sequence of parameter lists is evaluated left-to-right.
f:{x,y}
f[x:0][x+1]
0 1
+/[a;x] now means "a plus each-right x" (not over). This was a mistake in v0 that has been corrected in v1.
1 2 +/ 3 4 5
(4 5
5 6
6 7)
+/[1 2;3 4 5]
(4 5
5 6
6 7)
Similarly for each-left.
1 2 +\ 3 4 5
(4 5 6
5 6 7)
+\[1 2;3 4 5]
(4 5 6
5 6 7)
Note that in the process, we lost dyad over and dyad scan.
+/[a;x] is each-right
+\[a;x] is each-left.
No big deal. We can still do dyad over and dyad scan by using three args, but just ignoring the third one.
{z;x,y}/["";"asdf";nul]
"asdf"
{z;x,y}\["";"asdf";nul]
(""
,"a"
"as"
"asd"
"asdf")
Variable scope is lexical now. There is no dynamic scope as there was in v0. For example, something like this will no longer work since g can't resolve a.
g:{a}
f:{a:1;g`}
f`
value error
{a}
^
>
However, this will work, since g is defined lexically within f, where a is also defined.
f:{a:1;g:{a};g`}
f`
1
Some implementions of k limit lexical scope to one level, but in gk v1, it's not limited.
f:{a:1;g:{h:{a};h`};g`}
f`
1