SlideShare una empresa de Scribd logo
1 de 65
Descargar para leer sin conexión
Реактивные примитивы Warp9
@rystsov

2013

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Warp9 поддерживает реактивные переменные (Cell) и
реактивный списки (List).

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell: реактивные переменные

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / ctor

Реактивная переменная в warp9 может содержать какое-либо
значение или быть пустой, по умолчанию переменная
создается пустой.

var a = new C e l l ( ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / set

Для того чтобы положить в неё какое-либо значение
используется метод “set”

var a = new C e l l ( ) ;
a . set ( 42 ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / ctor(…)

Альтернатива - передать начальное значение в конструкторе

var a = new C e l l ( 4 2 ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / unset

В любой момент переменную можно сделать пустой

var a = new C e l l ( 4 2 ) ;
a . unset ( ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / unwrap

У переменной можно вызвать метод unwrap для того, чтобы
получить значение, которое она содержит

var a = new C e l l ( 4 2 ) ;
c o n s o l e . i n f o ( a . unwrap ( ) ) ;
// > 42

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / unwrap(…)

Если переменная была пустой - вылетит исключение, но
unwrap можно передать значение, которое следует вернуть,
если переменная пустая

c o n s o l e . i n f o ( new C e l l ( ) . unwrap ( 4 2 ) ) ;
// > 42

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / li
К существующей переменной можно применить функцию и
получить реактивную переменную, которая связана с первой
этой функцией: при изменении первой - автоматически
меняется вторая
var a = new C e l l ( ) ; // a is unset
var b = a . l i f t ( f u n c t i o n ( a ) {
r e t u r n a+2 ;
} ) ; // b is unset
a . s e t ( 1 ) ; // a contains 1, b contains 3
a . s e t ( 5 ) ; // a contains 1, b contains 7
a . u n s e t ( ) ; // a is unset , b is unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / li

Можно сказать, что для метода li выполняется
следующий закон

forall (f ,x) :
new C e l l ( x ) . l i f t ( f ) . unwrap ( ) == f ( x ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / li

У переменных полученных лифтингом (а врочем и любым
другим способом, кроме как вызовом конструктора Cell)
невозможно вызывать методы set, unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / coalesce

Еще один способо создать переменную - вызвать метод coalesce
и передать ему значение по умолчанию. Если исходная
переменная содержит какое-либо значение, то и новая
переменная будет его содержать, если исходная переменная
пустая, то новая переменная будет содержать значение по
умолчанию.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / coalesce

var a = new C e l l ( ) ;
var b = a . c o a l e s c e ( 4 2 ) ; // b contains 42
a . s e t ( 1 3 ) ; // a contains 13, b contains 13
a . u n s e t ( ) ; // a is unset , b contains 42

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / isSet

Вызов isSet вернет реактивную переменную, которая содержит
true если исходная не пустая, и false в ином случае.

var a = new C e l l ( ) ;
var b = a . i s S e t ( ) ; // b has false
a . s e t ( 1 3 ) ; // a has 13, b has true
a . u n s e t ( ) ; // a is unset , b has false

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…)

Другой способ создать переменную - вызвать метод when. Если
метод вызван с одним параметром, то параметр
рассматривается как фильтр. Если значение фильтра true, то
переменная будет содержать значение, совпадающее с
исходной, иначе переменная будет пустая.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…)

var a = new C e l l ( ) ;
var b = a . when ( f u n c t i o n ( a ) {
r e t u r n a >3 ;
} ) ; // b in unset
a . s e t ( 4 2 ) ; // a contains 42, b contains 42
a . s e t ( 4 ) ; // a contains 4, b contains 4
a . s e t ( 1 ) ; // a contains 1, b is unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when

Если в качестве фильтра передать не функцию, то значение
исходной переменной будет сравниваться с “фильтром” на
равенство.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …)

Так же методу when можно передать два параметра: фильтр и
трансформер, в это случае трансформер будет применен к
значению исходной переменной, если фильтр вернул true.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …)

var a = new C e l l ( ) ;
var b = a . when (
f u n c t i o n ( a ) { r e t u r n a >3 ; } ,
f u n c t i o n ( x ) { r e t u r n x+1 }
) ; // b in unset
a . s e t ( 4 2 ) ; // a contains 42, b contains 43
a . s e t ( 4 ) ; // a contains 4, b contains 5
a . s e t ( 1 ) ; // a contains 1, b is unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …)

На самом деле when с двумя аргументами практически
эквивалентен комбинации when и li
forall (f , t , cell ) :
c e l l . when ( f , t ) == c e l l . when ( f ) . l i f t ( t ) ;
Практически, потому что, как и в случае с одним параметром вместо фильтра и трансформера можно передать константы “фильтр’’ будет сравниваться с значением исходной
переменной, а “трансформер’’ вернет себя

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …)

var a = new C e l l ( ) ;
var b = a . when ( 4 2 , 1 3 ) ; // b in unset
a . s e t ( 4 2 ) ; // a contains 42, b contains 13
a . s e t ( 4 ) ; // a contains 4, b is unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …, …)

Последняя форма when - три аргумента: фильтр, трансформер
и алтернативный трансформер, работает точно так же как и
предыдущий, но если фильтр вернул false - применяется
алтернативный трансформер

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …, …)

var a = new C e l l ( ) ;
var b = a . when (
f u n c t i o n ( a ) { r e t u r n a >= 0 ; } ,
f u n c t i o n ( x ) { r e t u r n x+1 } ,
f u n c t i o n ( x ) { r e t u r n x−1 ; }
) ; // b in unset
a . s e t ( 0 ) ; // a contains 0, b contains 1
a . s e t (−1 ) ; // a contains -1, b contains -2
a . u n s e t ( ) ; // a is unset , b in unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …, …)

Вместо функций можно передать значения, в этом случае when
ведет себя как реактивный тернарный оператор

var a = new C e l l ( ) ;
var b = a . when ( t r u e , 1 , 0 ) ; // b is unset
a . s e t ( t r u e ) ; // a has true , b has 1
a . s e t ( f a l s e ) ; // a has false , b has 0
a . u n s e t ( ) ; // a is unset , b is unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / when(…, …, …)

За ислючения случая с константами, для when с тремя
аргументами выполняется закон

forall (c , f , t ,a) :
c . when ( f , t , a ) == c . l i f t ( f u n c t i o n ( c ) {
return f ( c ) ? t ( c ) : a ( c )
}) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind

Теперь рассмотрим последний метод Cell для создания новых
переменных - bind. Если бы мне пришлось выбирать из li,
when и bind - я бы выбрал bind, так как остальные легко из него
выводятся

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind

Bind’у на вход нужно передавать функцию. Эта функция
применяется к значению реактивной переменной и
возвращает Cell, в свою очередь bind возвращает новую
реактивную переменную, которая содержит то же значение,
что и переменная, которую вернула функция. Звучит cтрашно,
но надеюсь после примеров станет понятнее, но вначале…

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind

…закон
forall ( cell , f ) :
c e l l . b i n d ( f ) . unwrap ( )
==
f ( c e l l . unwrap ( ) ) . unwrap ( )

Сложно сказать зачем именно нужен этот монстр, так как
применяется он практически везде, но начнем по порядку, я
сказал, что с него помощью можно создать li…

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind / li

var c e l l = new C e l l ( ) ;
c e l l . l i f t = function ( f ) {
return t h i s . bind ( function ( x ) {
r e t u r n new C e l l ( f ( x ) ) ;
}) ;
}

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind / when

Теперь реализуем самую первую форму when

var c e l l = new C e l l ( ) ;
c e l l . when = f u n c t i o n ( f ) {
return t h i s . bind ( function ( x ) {
r e t u r n f ( x ) ? new C e l l ( x ) : new
Cell () ;
}) ;
};

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind / binary

Это далеко не все, например, с помощью bind можно взять
любую бинарную функцию от обычных значений и
возвращающую обычное значение, и применить её к двум
реактивным переменным и получить реактивную переменную

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Cell / bind / binary

var a = new C e l l ( 1 ) ;
var b = new C e l l ( 2 ) ;
var sum = a . b i n d ( f u n c t i o n ( a ) {
return b . bind ( function ( b ) {
r e t u r n new C e l l ( a+b ) ;
}) ;
} ) ; // sum has 3
a . s e t ( 2 ) ; // a has 2, b has 2, sum has 4
b . u n s e t ( ) ; // a has 2, b & sum are unset

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Монады

Как только вы скажете “Ага, я все понял!” - поздравляю, вы
только что познакомились с монадами

(черт, я же не хотел писать очередной туториал по монадам)

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Переходим к реактивным спискам (List)

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / ctor

Создаем список

var l i s t = new L i s t ( ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / ctor(…)

Списку можно передать начальные значения в конструкторе.

var l i s t = new L i s t ( [ 1 , 2 , 3 ] )

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / add

Кладем элементы в список

var l i s t = new L i s t ( ) ;
l i s t . add ( " Warp9 " ) ;
l i s t . add ( " React " ) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / unwrap

Получить содержимое списка можно через unwrap

var l i s t = new L i s t ( ) ;
l i s t . add ( " Warp9 " ) ;
c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ;
// [" Warp9 "]

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / remove
Метод add возвращает id элемента,
по которому его можно удалить
var l i s t = new L i s t ( ) ;
var w a r p Id = l i s t . add ( " Warp9 " ) ;
var r e a c t I d = l i s t . add ( " React " ) ;
c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ;
// > [" Warp9 ", " React "]
l i s t . remove ( r e a c t I d ) ;
c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ;
// > [" Warp9 "]

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / add(λ)

Часто id элемента должен содержаться внутри элемента,
поэтому методу add можно передать функцию, эта функция
будет вызвана, аргументом будет id, а в список будет добавлен
результат выполнения этой функции

var l i s t = new L i s t ( ) ;
l i s t . add ( f u n c t i o n ( i d ) {
r e t u r n { i d : i d , name : "Warp" } ;
}) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / removeWhich

Кроме remove, есть еще один метод - removeWhich, он
принимает предикат и удаляет из списка все элементы,
которые удоволетворяют этому предикату

var l i s t = new L i s t ( [ 1 , 2 , 3 ] ) ;
l i s t . removeWhich ( f u n c t i o n ( x ) {
return x < 2 ;
}) ;
// list contains 2,3

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / forEach
Так же в списке есть метод forEach, который ведет себя подобно
forEach массива

var l i s t = new L i s t ( [
" Warp9 " , " React "
]) ;
l i s t . forEach ( function ( x ) {
console . info ( x ) ;
}) ;
// > Warp9
// > React

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / remove, removeWhich и forEach

Методы remove, removeWhich и forEach не учитывают
реактивную природу списка и выполняются один раз в момент
вызова (не перевыполняются при добавлении новых
элементов)

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / li
Так же как и Cell, в List есть метод li, который создает новый
список и связывает его функцией с исходным, получается
реактивный map
var a = new L i s t ( ) ;
var b = a . l i f t ( f u n c t i o n ( x ) { r e t u r n x+2 ; } ) ;
// b. unwrap () ==[]
var i d 1 = a . add ( 1 ) ;
// a. unwrap () ==[1] , b. unwrap () ==[3]
var i d 2 = a . add ( 2 ) ;
// a. unwrap () ==[1 ,2] , b. unwrap () ==[3 ,4]
a . remove ( i d 1 ) ;
// a. unwrap () ==[2] , b. unwrap () ==[4]

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / liing

У списка полученный через лифтинг невозможно вызвать
методы add, remove, removeWhich

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce

Все, что я пока рассказал про списки есть и в Knockout и
ReactiveCoffee, но я обещал, что warp9 поддерживает больше, а
именно агрегацию. Начнем с метода reduce и попробуем
просуммировать элементы в списке и получить реактивное
значение суммы

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / sum
Первый параметр reduce - значение, которое соответствует
пустому списку, второе значение - функция, которая
занимается сверткой
var l i s t = new L i s t ( ) ;
var sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) {
r e t u r n a+b ;
}) ;
l i s t . add ( 4 1 ) ; // sum has 41
l i s t . add ( 1 ) ; // sum has 42
Как мы видим переменная sum получилась реактивной и
изменяется при изменении списка.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / count

После того, как мы посчитали сумму списка, давайте узнаем
кол-во элементов в нем

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / count

var l i s t = new L i s t ( ) ;
var c o u n t = l i s t . l i f t ( f u n c t i o n ( x ) {
return 1 ;
} ) . reduce ( 0 , function ( a , b ) {
r e t u r n a+b ;
}) ;
var i d 4 1 = l i s t . add ( 4 1 ) ; // count has 1
l i s t . add ( 1 ) ; // count has 2
l i s t . remove ( i d 4 1 ) ; // count has 1

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / count / wrap
Это работающий метод, правда требует создания нового
списка; существует чуть более эффективный путь

var c o u n t = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) {
r e t u r n a+b ;
}, {
wrap : f u n c t i o n ( x ) {
return 1 ;
}
}) ;

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / wrap

Функция wrap выполняется для каждого элемента списка и уже
её результат агрегируется, очевидно, что выполняется закон

f o r a l l ( l i s t , f , id , f o l d ) :
l i s t . r e d u c e ( i d , f o l d , { wrap : f } )
==
l i s t . l i f t ( f ) . reduce ( id , f o l d )

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / эффективность

Раз мы заговорили об эффективности, какова сложноть у
функции reduce, при добавлении элемента в список размера n?
В knockout она равна O(n), в warp9 она O(ln n). Но делать даже
ln n вычислений на каждое добавление элемента в список,
чтобы инкременировать одно значение (sum или count)
странно, поэтому в warp9 есть другой путь

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduceMonoid

Если посмотреть на реализацию функции reduce, видно, что
вызов list.reduce(id, fold) раскрывается в
l i s t . re d uce Mo n o i d ( {
i d e n t i t y : id ,
add : f o l d
}) ;
Искушенный читатель догадается, что раз есть Monoid, то есть
и Group, и будет прав

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduceGroup
Пример с суммой элементов списка:
var sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) {
r e t u r n a+b ;
}) ;
можно переписать так:
var sum = l i s t . r e d u c e G r o u p ( {
i d e n t i t y : function ( ) { return 0 } ,
add : f u n c t i o n ( a , b ) { r e t u r n a+b ; } ,
i n v e r t : f u n c t i o n ( x ) { r e t u r n −x ; }
}) ;
И сложность, магическим образом, упадет с O(ln n) до O(1).
Аналогично можно посчитать и кол-во элементов в списке
..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce∗

Понять, можно ли использоват reduce∗ и какой вариант
использовать достаточно просто. Если агрегат списка не
зависит от порядка элементов в списке и по посчитанному
агрегату и вставляемому элементу вы можете посчитать
значение нового агрегата, то агрегат можно выразить через
reduce∗ функции.

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduceGroup

Если зная агрегат и удаляемый элемент вы сможете посчитать
значение, которое совпадет с агрегатом нового списка, то
агрегат можно выразить через reduceGroup

Очевидно, что агрегаты sum и count попадают под оба
определения, а значит мы можем для них использовать
reduceGroup

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduceMonoid

Может сложится впечатление, что любой коммутативный
агрегат попадает под это определение,
но на самом деле это не так.
Допустим, мы хотим посчитать логическое & от списка bool.
Данная задача попадает под reduceMonoid , но не под
reduceGroup
Зная, что агрегат false и зная, что удаляемый элемент тоже
false, мы не обладаем информацией, есть ли в списке еще false,
а от этого зависит будет ли агрегат false или true

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce(&)

Хотя я вру

На самом деле, задачу логического & можно свести к
reduceGroup. Достаточно каждое true рассматривать как пару
(1,1), каждое false как (0,1), при добавлении элемента покомпонентно складывать пары, при удалении - удалять.
Если в каждой компоненте одинаковое число - значит
значение агрегата true, иначе false. Единственная проблема - в
качестве результата будет пара, а не boolean

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / unwrap

Оказывается в warp9 это не проблема, так как есть опция
unwrap

После того как reduce отработает, вызывается unwrap и
преобразовывает результат в нужный тип..

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce / unwrap
l i s t . reduceGroup ( {
i d e n t i t y : function ( ) { return [ 0 , 0 ] ; } ,
add : f u n c t i o n ( x , y ) {
r e t u r n [ x [ 0 ]+ y [ 0 ] , x [ 1 ]+ y [ 1 ] ] ;
},
invert : function ( x ) {
r e t u r n [−x [ 0 ] , − x [ 1 ] ] ;
}
} ,{
wrap : f u n c t i o n ( x ) {
return x ? [ 1 , 1 ] : [ 0 , 1 ] ;
},
unwrap : f u n c t i o n ( x ) {
r e t u r n x [ 0 ]== x [ 1 ] ;
}
}) ;
..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce∗ / Cell

Особенностью работы reduce∗ является то, что он учитывает не
только реактивность списка, но и реактивность переменных…

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce∗ / Cell
var
var
var
var

l i s t = new L i s t ( ) ;
i t 1 = new C e l l ( 0 ) ;
i t 2 = new C e l l ( 1 ) ;
sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) {
return a + b ;
} ) ; // sum has 0
var i t I d 1 = l i s t . add ( i t 1 ) ; // sum has 0
var i t I d 2 = l i s t . add ( i t 2 ) ; // sum has 1
i t 1 . s e t ( 5 ) ; // sum has 6
i t 2 . s e t ( 2 ) ; // sum has 7
i t 1 . u n s e t ( ) ; // sum is unset
l i s t . remove ( i t I d 1 ) ; // sum has 2

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
List / reduce∗ / Cell / ignoreUnset
Видно, что если внутри реактивный списока есть пустая
переменная, то результат агрегации будет пустым. Это
поведение можно изменить: достаточно в reduce передать
опцию ignoreUnset - все пустые элементы будут заменяться на
единичный элемент моноида или группы
var
var
var
var

i t e m 1 = new C e l l ( ) ;
i t e m 2 = new C e l l ( 1 ) ;
l i s t = new L i s t ( [ i t e m 1 , i t e m 2 ] ) ;
sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) {
return a + b ;

}, {
ignoreUnset : true
} ) ; // sum has 1
i t e m 1 . s e t ( 3 ) ; // sum has 4

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.
Спасибо

Wow! 65 слайдов, вы это
выдержали!

..

.
..

.
..

.

. . . . . . . . . . . .
.. .. .. .. .. .. .. .. .. .. .. .. ..

.
..

.
..

.
..

.
..

.

Más contenido relacionado

Destacado

Front cover analysis
Front cover analysisFront cover analysis
Front cover analysisyumm
 
Outlining protocols 01
Outlining protocols 01Outlining protocols 01
Outlining protocols 01jdaconceicao
 
Question 1
Question 1Question 1
Question 1Kamamor
 
Inaugurarea Salii de Lectura Raisa Caldare
Inaugurarea Salii de Lectura Raisa CaldareInaugurarea Salii de Lectura Raisa Caldare
Inaugurarea Salii de Lectura Raisa CaldareAdela Negura
 
7 анги
7 анги7 анги
7 ангиenhmonh
 
Estrategias y tecnicas de estudio noviembre 2015
Estrategias y  tecnicas de estudio noviembre 2015Estrategias y  tecnicas de estudio noviembre 2015
Estrategias y tecnicas de estudio noviembre 2015JFCOPGLEZ
 
Classroom Management
Classroom ManagementClassroom Management
Classroom ManagementJane Wolff
 
Zeta function for perturbed surfaces of revolution
Zeta function for perturbed surfaces of revolutionZeta function for perturbed surfaces of revolution
Zeta function for perturbed surfaces of revolutionPedro Morales
 
Presentation1
Presentation1Presentation1
Presentation1akwheaton
 
Open Educational Resources (Wheaton College 3/16/14)
Open Educational Resources (Wheaton College 3/16/14)Open Educational Resources (Wheaton College 3/16/14)
Open Educational Resources (Wheaton College 3/16/14)Nicole Allen
 
Foil insulation radiant barrier from sinolam
Foil insulation radiant barrier from sinolamFoil insulation radiant barrier from sinolam
Foil insulation radiant barrier from sinolamWWW.SINOLAM.COM WEBSITE
 
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...Azimut Yacht Club
 
Wolontariat społeczny
Wolontariat społecznyWolontariat społeczny
Wolontariat społecznywolontariat
 

Destacado (20)

Superlative
SuperlativeSuperlative
Superlative
 
Front cover analysis
Front cover analysisFront cover analysis
Front cover analysis
 
Outlining protocols 01
Outlining protocols 01Outlining protocols 01
Outlining protocols 01
 
Programa formación cje
Programa formación cjePrograma formación cje
Programa formación cje
 
Question 1
Question 1Question 1
Question 1
 
Inaugurarea Salii de Lectura Raisa Caldare
Inaugurarea Salii de Lectura Raisa CaldareInaugurarea Salii de Lectura Raisa Caldare
Inaugurarea Salii de Lectura Raisa Caldare
 
7 анги
7 анги7 анги
7 анги
 
Estrategias y tecnicas de estudio noviembre 2015
Estrategias y  tecnicas de estudio noviembre 2015Estrategias y  tecnicas de estudio noviembre 2015
Estrategias y tecnicas de estudio noviembre 2015
 
Classroom Management
Classroom ManagementClassroom Management
Classroom Management
 
Zeta function for perturbed surfaces of revolution
Zeta function for perturbed surfaces of revolutionZeta function for perturbed surfaces of revolution
Zeta function for perturbed surfaces of revolution
 
Mali
MaliMali
Mali
 
Presentation1
Presentation1Presentation1
Presentation1
 
Open Educational Resources (Wheaton College 3/16/14)
Open Educational Resources (Wheaton College 3/16/14)Open Educational Resources (Wheaton College 3/16/14)
Open Educational Resources (Wheaton College 3/16/14)
 
Foil insulation radiant barrier from sinolam
Foil insulation radiant barrier from sinolamFoil insulation radiant barrier from sinolam
Foil insulation radiant barrier from sinolam
 
Cvc a
Cvc aCvc a
Cvc a
 
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...
AZIMUT Azimut 46, 1997, 199.000 € For Sale Brochure. Presented By azimut-yach...
 
Ruski oligarsi
Ruski oligarsiRuski oligarsi
Ruski oligarsi
 
Wolontariat społeczny
Wolontariat społecznyWolontariat społeczny
Wolontariat społeczny
 
Simt advertment
Simt advertmentSimt advertment
Simt advertment
 
Giselapower
GiselapowerGiselapower
Giselapower
 

Warp9: reactive primitives

  • 1. Реактивные примитивы Warp9 @rystsov 2013 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 2. Warp9 поддерживает реактивные переменные (Cell) и реактивный списки (List). .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 3. Cell: реактивные переменные .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 4. Cell / ctor Реактивная переменная в warp9 может содержать какое-либо значение или быть пустой, по умолчанию переменная создается пустой. var a = new C e l l ( ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 5. Cell / set Для того чтобы положить в неё какое-либо значение используется метод “set” var a = new C e l l ( ) ; a . set ( 42 ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 6. Cell / ctor(…) Альтернатива - передать начальное значение в конструкторе var a = new C e l l ( 4 2 ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 7. Cell / unset В любой момент переменную можно сделать пустой var a = new C e l l ( 4 2 ) ; a . unset ( ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 8. Cell / unwrap У переменной можно вызвать метод unwrap для того, чтобы получить значение, которое она содержит var a = new C e l l ( 4 2 ) ; c o n s o l e . i n f o ( a . unwrap ( ) ) ; // > 42 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 9. Cell / unwrap(…) Если переменная была пустой - вылетит исключение, но unwrap можно передать значение, которое следует вернуть, если переменная пустая c o n s o l e . i n f o ( new C e l l ( ) . unwrap ( 4 2 ) ) ; // > 42 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 10. Cell / li К существующей переменной можно применить функцию и получить реактивную переменную, которая связана с первой этой функцией: при изменении первой - автоматически меняется вторая var a = new C e l l ( ) ; // a is unset var b = a . l i f t ( f u n c t i o n ( a ) { r e t u r n a+2 ; } ) ; // b is unset a . s e t ( 1 ) ; // a contains 1, b contains 3 a . s e t ( 5 ) ; // a contains 1, b contains 7 a . u n s e t ( ) ; // a is unset , b is unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 11. Cell / li Можно сказать, что для метода li выполняется следующий закон forall (f ,x) : new C e l l ( x ) . l i f t ( f ) . unwrap ( ) == f ( x ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 12. Cell / li У переменных полученных лифтингом (а врочем и любым другим способом, кроме как вызовом конструктора Cell) невозможно вызывать методы set, unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 13. Cell / coalesce Еще один способо создать переменную - вызвать метод coalesce и передать ему значение по умолчанию. Если исходная переменная содержит какое-либо значение, то и новая переменная будет его содержать, если исходная переменная пустая, то новая переменная будет содержать значение по умолчанию. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 14. Cell / coalesce var a = new C e l l ( ) ; var b = a . c o a l e s c e ( 4 2 ) ; // b contains 42 a . s e t ( 1 3 ) ; // a contains 13, b contains 13 a . u n s e t ( ) ; // a is unset , b contains 42 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 15. Cell / isSet Вызов isSet вернет реактивную переменную, которая содержит true если исходная не пустая, и false в ином случае. var a = new C e l l ( ) ; var b = a . i s S e t ( ) ; // b has false a . s e t ( 1 3 ) ; // a has 13, b has true a . u n s e t ( ) ; // a is unset , b has false .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 16. Cell / when(…) Другой способ создать переменную - вызвать метод when. Если метод вызван с одним параметром, то параметр рассматривается как фильтр. Если значение фильтра true, то переменная будет содержать значение, совпадающее с исходной, иначе переменная будет пустая. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 17. Cell / when(…) var a = new C e l l ( ) ; var b = a . when ( f u n c t i o n ( a ) { r e t u r n a >3 ; } ) ; // b in unset a . s e t ( 4 2 ) ; // a contains 42, b contains 42 a . s e t ( 4 ) ; // a contains 4, b contains 4 a . s e t ( 1 ) ; // a contains 1, b is unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 18. Cell / when Если в качестве фильтра передать не функцию, то значение исходной переменной будет сравниваться с “фильтром” на равенство. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 19. Cell / when(…, …) Так же методу when можно передать два параметра: фильтр и трансформер, в это случае трансформер будет применен к значению исходной переменной, если фильтр вернул true. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 20. Cell / when(…, …) var a = new C e l l ( ) ; var b = a . when ( f u n c t i o n ( a ) { r e t u r n a >3 ; } , f u n c t i o n ( x ) { r e t u r n x+1 } ) ; // b in unset a . s e t ( 4 2 ) ; // a contains 42, b contains 43 a . s e t ( 4 ) ; // a contains 4, b contains 5 a . s e t ( 1 ) ; // a contains 1, b is unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 21. Cell / when(…, …) На самом деле when с двумя аргументами практически эквивалентен комбинации when и li forall (f , t , cell ) : c e l l . when ( f , t ) == c e l l . when ( f ) . l i f t ( t ) ; Практически, потому что, как и в случае с одним параметром вместо фильтра и трансформера можно передать константы “фильтр’’ будет сравниваться с значением исходной переменной, а “трансформер’’ вернет себя .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 22. Cell / when(…, …) var a = new C e l l ( ) ; var b = a . when ( 4 2 , 1 3 ) ; // b in unset a . s e t ( 4 2 ) ; // a contains 42, b contains 13 a . s e t ( 4 ) ; // a contains 4, b is unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 23. Cell / when(…, …, …) Последняя форма when - три аргумента: фильтр, трансформер и алтернативный трансформер, работает точно так же как и предыдущий, но если фильтр вернул false - применяется алтернативный трансформер .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 24. Cell / when(…, …, …) var a = new C e l l ( ) ; var b = a . when ( f u n c t i o n ( a ) { r e t u r n a >= 0 ; } , f u n c t i o n ( x ) { r e t u r n x+1 } , f u n c t i o n ( x ) { r e t u r n x−1 ; } ) ; // b in unset a . s e t ( 0 ) ; // a contains 0, b contains 1 a . s e t (−1 ) ; // a contains -1, b contains -2 a . u n s e t ( ) ; // a is unset , b in unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 25. Cell / when(…, …, …) Вместо функций можно передать значения, в этом случае when ведет себя как реактивный тернарный оператор var a = new C e l l ( ) ; var b = a . when ( t r u e , 1 , 0 ) ; // b is unset a . s e t ( t r u e ) ; // a has true , b has 1 a . s e t ( f a l s e ) ; // a has false , b has 0 a . u n s e t ( ) ; // a is unset , b is unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 26. Cell / when(…, …, …) За ислючения случая с константами, для when с тремя аргументами выполняется закон forall (c , f , t ,a) : c . when ( f , t , a ) == c . l i f t ( f u n c t i o n ( c ) { return f ( c ) ? t ( c ) : a ( c ) }) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 27. Cell / bind Теперь рассмотрим последний метод Cell для создания новых переменных - bind. Если бы мне пришлось выбирать из li, when и bind - я бы выбрал bind, так как остальные легко из него выводятся .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 28. Cell / bind Bind’у на вход нужно передавать функцию. Эта функция применяется к значению реактивной переменной и возвращает Cell, в свою очередь bind возвращает новую реактивную переменную, которая содержит то же значение, что и переменная, которую вернула функция. Звучит cтрашно, но надеюсь после примеров станет понятнее, но вначале… .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 29. Cell / bind …закон forall ( cell , f ) : c e l l . b i n d ( f ) . unwrap ( ) == f ( c e l l . unwrap ( ) ) . unwrap ( ) Сложно сказать зачем именно нужен этот монстр, так как применяется он практически везде, но начнем по порядку, я сказал, что с него помощью можно создать li… .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 30. Cell / bind / li var c e l l = new C e l l ( ) ; c e l l . l i f t = function ( f ) { return t h i s . bind ( function ( x ) { r e t u r n new C e l l ( f ( x ) ) ; }) ; } .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 31. Cell / bind / when Теперь реализуем самую первую форму when var c e l l = new C e l l ( ) ; c e l l . when = f u n c t i o n ( f ) { return t h i s . bind ( function ( x ) { r e t u r n f ( x ) ? new C e l l ( x ) : new Cell () ; }) ; }; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 32. Cell / bind / binary Это далеко не все, например, с помощью bind можно взять любую бинарную функцию от обычных значений и возвращающую обычное значение, и применить её к двум реактивным переменным и получить реактивную переменную .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 33. Cell / bind / binary var a = new C e l l ( 1 ) ; var b = new C e l l ( 2 ) ; var sum = a . b i n d ( f u n c t i o n ( a ) { return b . bind ( function ( b ) { r e t u r n new C e l l ( a+b ) ; }) ; } ) ; // sum has 3 a . s e t ( 2 ) ; // a has 2, b has 2, sum has 4 b . u n s e t ( ) ; // a has 2, b & sum are unset .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 34. Монады Как только вы скажете “Ага, я все понял!” - поздравляю, вы только что познакомились с монадами (черт, я же не хотел писать очередной туториал по монадам) .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 35. Переходим к реактивным спискам (List) .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 36. List / ctor Создаем список var l i s t = new L i s t ( ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 37. List / ctor(…) Списку можно передать начальные значения в конструкторе. var l i s t = new L i s t ( [ 1 , 2 , 3 ] ) .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 38. List / add Кладем элементы в список var l i s t = new L i s t ( ) ; l i s t . add ( " Warp9 " ) ; l i s t . add ( " React " ) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 39. List / unwrap Получить содержимое списка можно через unwrap var l i s t = new L i s t ( ) ; l i s t . add ( " Warp9 " ) ; c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ; // [" Warp9 "] .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 40. List / remove Метод add возвращает id элемента, по которому его можно удалить var l i s t = new L i s t ( ) ; var w a r p Id = l i s t . add ( " Warp9 " ) ; var r e a c t I d = l i s t . add ( " React " ) ; c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ; // > [" Warp9 ", " React "] l i s t . remove ( r e a c t I d ) ; c o n s o l e . i n f o ( l i s t . unwrap ( ) ) ; // > [" Warp9 "] .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 41. List / add(λ) Часто id элемента должен содержаться внутри элемента, поэтому методу add можно передать функцию, эта функция будет вызвана, аргументом будет id, а в список будет добавлен результат выполнения этой функции var l i s t = new L i s t ( ) ; l i s t . add ( f u n c t i o n ( i d ) { r e t u r n { i d : i d , name : "Warp" } ; }) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 42. List / removeWhich Кроме remove, есть еще один метод - removeWhich, он принимает предикат и удаляет из списка все элементы, которые удоволетворяют этому предикату var l i s t = new L i s t ( [ 1 , 2 , 3 ] ) ; l i s t . removeWhich ( f u n c t i o n ( x ) { return x < 2 ; }) ; // list contains 2,3 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 43. List / forEach Так же в списке есть метод forEach, который ведет себя подобно forEach массива var l i s t = new L i s t ( [ " Warp9 " , " React " ]) ; l i s t . forEach ( function ( x ) { console . info ( x ) ; }) ; // > Warp9 // > React .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 44. List / remove, removeWhich и forEach Методы remove, removeWhich и forEach не учитывают реактивную природу списка и выполняются один раз в момент вызова (не перевыполняются при добавлении новых элементов) .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 45. List / li Так же как и Cell, в List есть метод li, который создает новый список и связывает его функцией с исходным, получается реактивный map var a = new L i s t ( ) ; var b = a . l i f t ( f u n c t i o n ( x ) { r e t u r n x+2 ; } ) ; // b. unwrap () ==[] var i d 1 = a . add ( 1 ) ; // a. unwrap () ==[1] , b. unwrap () ==[3] var i d 2 = a . add ( 2 ) ; // a. unwrap () ==[1 ,2] , b. unwrap () ==[3 ,4] a . remove ( i d 1 ) ; // a. unwrap () ==[2] , b. unwrap () ==[4] .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 46. List / liing У списка полученный через лифтинг невозможно вызвать методы add, remove, removeWhich .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 47. List / reduce Все, что я пока рассказал про списки есть и в Knockout и ReactiveCoffee, но я обещал, что warp9 поддерживает больше, а именно агрегацию. Начнем с метода reduce и попробуем просуммировать элементы в списке и получить реактивное значение суммы .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 48. List / reduce / sum Первый параметр reduce - значение, которое соответствует пустому списку, второе значение - функция, которая занимается сверткой var l i s t = new L i s t ( ) ; var sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) { r e t u r n a+b ; }) ; l i s t . add ( 4 1 ) ; // sum has 41 l i s t . add ( 1 ) ; // sum has 42 Как мы видим переменная sum получилась реактивной и изменяется при изменении списка. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 49. List / reduce / count После того, как мы посчитали сумму списка, давайте узнаем кол-во элементов в нем .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 50. List / reduce / count var l i s t = new L i s t ( ) ; var c o u n t = l i s t . l i f t ( f u n c t i o n ( x ) { return 1 ; } ) . reduce ( 0 , function ( a , b ) { r e t u r n a+b ; }) ; var i d 4 1 = l i s t . add ( 4 1 ) ; // count has 1 l i s t . add ( 1 ) ; // count has 2 l i s t . remove ( i d 4 1 ) ; // count has 1 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 51. List / reduce / count / wrap Это работающий метод, правда требует создания нового списка; существует чуть более эффективный путь var c o u n t = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) { r e t u r n a+b ; }, { wrap : f u n c t i o n ( x ) { return 1 ; } }) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 52. List / wrap Функция wrap выполняется для каждого элемента списка и уже её результат агрегируется, очевидно, что выполняется закон f o r a l l ( l i s t , f , id , f o l d ) : l i s t . r e d u c e ( i d , f o l d , { wrap : f } ) == l i s t . l i f t ( f ) . reduce ( id , f o l d ) .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 53. List / reduce / эффективность Раз мы заговорили об эффективности, какова сложноть у функции reduce, при добавлении элемента в список размера n? В knockout она равна O(n), в warp9 она O(ln n). Но делать даже ln n вычислений на каждое добавление элемента в список, чтобы инкременировать одно значение (sum или count) странно, поэтому в warp9 есть другой путь .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 54. List / reduceMonoid Если посмотреть на реализацию функции reduce, видно, что вызов list.reduce(id, fold) раскрывается в l i s t . re d uce Mo n o i d ( { i d e n t i t y : id , add : f o l d }) ; Искушенный читатель догадается, что раз есть Monoid, то есть и Group, и будет прав .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 55. List / reduceGroup Пример с суммой элементов списка: var sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) { r e t u r n a+b ; }) ; можно переписать так: var sum = l i s t . r e d u c e G r o u p ( { i d e n t i t y : function ( ) { return 0 } , add : f u n c t i o n ( a , b ) { r e t u r n a+b ; } , i n v e r t : f u n c t i o n ( x ) { r e t u r n −x ; } }) ; И сложность, магическим образом, упадет с O(ln n) до O(1). Аналогично можно посчитать и кол-во элементов в списке .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 56. List / reduce∗ Понять, можно ли использоват reduce∗ и какой вариант использовать достаточно просто. Если агрегат списка не зависит от порядка элементов в списке и по посчитанному агрегату и вставляемому элементу вы можете посчитать значение нового агрегата, то агрегат можно выразить через reduce∗ функции. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 57. List / reduceGroup Если зная агрегат и удаляемый элемент вы сможете посчитать значение, которое совпадет с агрегатом нового списка, то агрегат можно выразить через reduceGroup Очевидно, что агрегаты sum и count попадают под оба определения, а значит мы можем для них использовать reduceGroup .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 58. List / reduceMonoid Может сложится впечатление, что любой коммутативный агрегат попадает под это определение, но на самом деле это не так. Допустим, мы хотим посчитать логическое & от списка bool. Данная задача попадает под reduceMonoid , но не под reduceGroup Зная, что агрегат false и зная, что удаляемый элемент тоже false, мы не обладаем информацией, есть ли в списке еще false, а от этого зависит будет ли агрегат false или true .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 59. List / reduce(&) Хотя я вру На самом деле, задачу логического & можно свести к reduceGroup. Достаточно каждое true рассматривать как пару (1,1), каждое false как (0,1), при добавлении элемента покомпонентно складывать пары, при удалении - удалять. Если в каждой компоненте одинаковое число - значит значение агрегата true, иначе false. Единственная проблема - в качестве результата будет пара, а не boolean .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 60. List / reduce / unwrap Оказывается в warp9 это не проблема, так как есть опция unwrap После того как reduce отработает, вызывается unwrap и преобразовывает результат в нужный тип.. .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 61. List / reduce / unwrap l i s t . reduceGroup ( { i d e n t i t y : function ( ) { return [ 0 , 0 ] ; } , add : f u n c t i o n ( x , y ) { r e t u r n [ x [ 0 ]+ y [ 0 ] , x [ 1 ]+ y [ 1 ] ] ; }, invert : function ( x ) { r e t u r n [−x [ 0 ] , − x [ 1 ] ] ; } } ,{ wrap : f u n c t i o n ( x ) { return x ? [ 1 , 1 ] : [ 0 , 1 ] ; }, unwrap : f u n c t i o n ( x ) { r e t u r n x [ 0 ]== x [ 1 ] ; } }) ; .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 62. List / reduce∗ / Cell Особенностью работы reduce∗ является то, что он учитывает не только реактивность списка, но и реактивность переменных… .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 63. List / reduce∗ / Cell var var var var l i s t = new L i s t ( ) ; i t 1 = new C e l l ( 0 ) ; i t 2 = new C e l l ( 1 ) ; sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) { return a + b ; } ) ; // sum has 0 var i t I d 1 = l i s t . add ( i t 1 ) ; // sum has 0 var i t I d 2 = l i s t . add ( i t 2 ) ; // sum has 1 i t 1 . s e t ( 5 ) ; // sum has 6 i t 2 . s e t ( 2 ) ; // sum has 7 i t 1 . u n s e t ( ) ; // sum is unset l i s t . remove ( i t I d 1 ) ; // sum has 2 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 64. List / reduce∗ / Cell / ignoreUnset Видно, что если внутри реактивный списока есть пустая переменная, то результат агрегации будет пустым. Это поведение можно изменить: достаточно в reduce передать опцию ignoreUnset - все пустые элементы будут заменяться на единичный элемент моноида или группы var var var var i t e m 1 = new C e l l ( ) ; i t e m 2 = new C e l l ( 1 ) ; l i s t = new L i s t ( [ i t e m 1 , i t e m 2 ] ) ; sum = l i s t . r e d u c e ( 0 , f u n c t i o n ( a , b ) { return a + b ; }, { ignoreUnset : true } ) ; // sum has 1 i t e m 1 . s e t ( 3 ) ; // sum has 4 .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .
  • 65. Спасибо Wow! 65 слайдов, вы это выдержали! .. . .. . .. . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. . .. . .. . .. . .. .