December 13, 2013

Язык K: Отображение графического интерфейса из данных

Оригинал тут: http://habrahabr.ru/post/205844/

Я как всегда об APL, а точнее о старой версии языка K, которая содержала в себе GUI, с весьма необычным подходом к нему.

К сожалению новые версии языка K решили сосредоточиться только на обработке данных и исключили GUI, так что данный подход остался в истории, однако может быть кто-то подскажет аналогичные современные фреймворки - было бы очень интересно посмотреть.

Начнём. Краткое описание API, которое нам доступно:
`show Язык K: Отображение графического интерфейса из данных — Teletype v show variable v
`hide Язык K: Отображение графического интерфейса из данных — Teletype v hide variable v

Это всё, больше нет ничего. Т.е. основная особенность - что GUI в K это прямое отображение данных в памяти. А теперь как с этим можно удобно работать.

Для начала попробуем самое простое:
C:\>k
K 3.1 2004-01-28 Copyright (C) 1993-2004 Kx Systems
WIN32 2CPU 4030MB ws-1341.x.com 0 EVAL
a:10 _draw 100 / list
a
20 51 12 34 31 51 29 35 17 89
`show Язык K: Отображение графического интерфейса из данных — Teletype a

На экране появляется таблица со списком

Любое значение редактируемое, меняем 35 на 135 и это изменение сразу меняет значение в списке:
a
20 51 12 34 31 51 29 135 17 89

Если меняем значение в списке, то оно тутже обновляется в интерфейсе.

Вывести небольшой список не проблема, а что если данных будет много? пусть будет очень много:
a:(10 10000000) _draw 100 / 10 списков по 10 миллионов каждый
`show Язык K: Отображение графического интерфейса из данных — Teletype a

Никаких проблем - всё быстро отображается, прокручивается и редактируется.

Именуем колонки: логично что имя колонки - это ключ, а список - значение из hashtable:
t:.((`a;10 _draw 100;);(`b;10 _draw 100))
`show Язык K: Отображение графического интерфейса из данных — Teletype t

Но это всё очень просто, смотрим что есть ещё. аттрибуты и триггеры - по сути это просто ключ-значение в hashtable, привязанном к переменной, в зависимости от того есть такое значение или нет - происходят разные действия:

Самый простой пример: добавление label.
val:10
val..l:"Input field"
`show Язык K: Отображение графического интерфейса из данных — Teletype val

Следующие аттрибуты влияют на отображение в gui:
Display attributes (for variables that have class).
x width integer(KFONT width)
y height integer(KFONT height)
a arrangement nest of symbols(class `form)
o options list of symbols(class `radio)
l label string
kl click label string (also klr)

Data-display attributes (for variables that have class `data).
functions (monadic, constant or array) default
e editable 0 or 1 1
f format string from data 11$(11.2$)
g getdata data from string 0$ etc.
u update update[old;new] :
fg foreground integer(rrggbb) 0
bg background integer(rrggbb) -1(808080)

expressions/events (strings)
ins, del, f1 ... f12, ctrl_a ... ctrl_z
k, kr, kk click, click right, double click(precludes e)

c class(display) symbol
`data(default) atom, list, dict, list of lists, dict of lists
`chart as above where atom is list of y values
`plot as above where atom is matrix of (x;y) values
`check 0 or 1
`radio symbol (one of ..o; see below)
`button expression or dictionary of expressions
`form dictionary of entries of any class(incl. `form)

Не буду описывать детальные особенности каждого из аттрибутов, так как большинство из них очевидны из описания и делать подобие мануала не хочется.

Для простоты создания словаря - язык позволяет работать в контексте внутри словаря - т.е. аналогия с папками и модулями, которые просто хэш-таблицы:
\d form
val:100
incr:"val+:1"
incr..l:"Increment"
decr:"val-:1"
decr..l:"Decrement"
incr..c: decr..c: `button
\d ^
form / хэш ключей-значений и хэшей аттрибутов.
.((`val;100;)
(`incr
"val+:1"
.((`l;"Increment";)
(`c;`button;)))
(`decr
"val-:1"
.((`l;"Decrement";)
(`c;`button;))))

Аттрибутом ..a установим порядок отображения.
form..a:`incr`val`decr
form.val..e:0 / отключаем редактирование значения.

..a может быть любой формы. Например добавим пару кнопок.
form.incr10:"val+:10"
form.decr10:"val+:10"
form.incr10..c : form.decr10..c: `button
form..a:(,`incr;`decr10`val`incr10;,`decr)
form..a
(,`incr
`decr10 `val `incr10
,`decr)

Т.е. смысл в том, что всё GUI описывается примитивными структурами языка и является их же прямым отображением. Можно включать hash в hash, т.е. включать форму в форму компонуя элементы и тд.

Ну а теперь самое интересное, а именно несколько примеров:

Калькулятор:
calc..a:(`exp
`va`vb`vc`vd
`n0`n1`n2`n3
`n4`n5`n6`n7
`n8`n9`lp`rp
`fa`fs`fm`fd`fe
`eval`clear) / порядок элементов на форме
calc:@[_n;1_-1_ calc..a;:[;"exp,:(~_v)`l"]] / expressions
calc[.;`l]:"abcd0123456789()+-*%:" / labels
calc.eval:"exp:5:. exp"
calc.clear:"exp:\"\""
calc[.;`c]:`button
calc.exp:calc.exp..l:""

`show Язык K: Отображение графического интерфейса из данных — Teletype calc

Можно рисовать графики:
p..c:`chart
p:(5 5) _draw 100
`show Язык K: Отображение графического интерфейса из данных — Teletype p

Или высокохудожественная мазмя:
`show$.,((`p;({[x] (2 30)_draw 30}'!10);.,(`c;`plot;)))

А теперь удивительная вещь, которая когда-то так удивила своей краткостью и понятностью (при минимальном знании словаря конечно), что я решил начать изучать K глубже:

Broadcast сервер:
d:10 _draw 10

w:!0 / empty client list

.m.g:{w,:_w;d} / return data
.m.c:"w@:&~w=_w" / retain clients
.m.s:{. x;w 3:\:x;} / (log `l 5:,x) apply and broadcast

\m i 1 listen on port 1

Клиент:
h:3:(`;1) / connect to server
d:h 4:_n / get database
d..t:"if[0=_w;h 3:(_v;_i;:;_v ._i)]" / send my updates

`show Язык K: Отображение графического интерфейса из данных — Teletype d

Вот эти несколько строчек создают сервер с простым списком в качестве данных. А сколько угодно клиентов присоединяются к нему и имеют совместно редактируемый список с обновление в реальном времени, всего 9 строчек. Подобный код (без GUI конечно) в настоящее время используется на многих крупнейших биржах для транспортных и балансировочных узлов, которые обслуживают инстансы баз данных Q (новая версия K).

Картинка конечно не передаёт того как это работает в динамике.