Call Me Maybe

<- Радослав Георгиев

Ще си говорим за контейнери.

Защо правим null / undefined checking?

var head = function(xs) { return xs[0]; }
var last = compose(head, reverse);

p(head([])); // undefined
p(last([])); // undefined

Винаги проверяваме с if, за да не гръмне.

if(!nullOrUndefined(arg1)) {
    // do something
} else {
    // stop the world!
}

if(!nullOrUndefined(arg2)) {
    // do something
} else {
    // stop the world!
}

Ще се запознаем с концепцията за кутия.

Контейнера ще държи каквато и да е стойност.

Ще има метод map(f), чрез който ще получаваме нов контейнер с променената стойност.

Прост контейнер:

function Container(x) {
  this.__value = x;
}

Container.of = function(x) {
  return new Container(x);
}

Container.prototype.map = function(f) {
  return Container.of(f(this.__value));
}

Примери:

var inc = add(1);
var a = Container.of(5);
var b = a.map(b);

console.log(b.__value); // 6
var today = Container.of(["Beer", "JS"]);
today = today.map(concat).map(capitalize);

console.log(today.__value); // "BEERJS"

Ще ползваме наш map и compose

var map = ramda.curry(function(f, xs) {
    return xs.map(f);
});

var compose = ramda.compose;

Примерите стават така:

var a = Container.of(5);
var b = map(inc, a);
console.log(b.__value); // 6
var today = Container.of(["Beer", "JS"]);
today = compose(map(capitalize), map(concat))(today);

console.log(today.__value); // "BEERJS"

map отваря кутията, променя стойността и връща нова кутия

 

map :: Function -> Container a -> Container b
capitalize :: String -> String
// a :: Container String
var a = Container.of("beer");

// b :: Container String
var b = map(capitalize, a); // Container.of("BEER")ч

Кутия за стойност, която може да променяме чрез map се нарична Functor.

Container Functor-а "Идентитет"

Може да направим следното нещо с един functor:

compose(map(capitalize), map(concat))
==
map(compose(capitalize, concat))

Maybe = Или стойност или нищо.

var a = Maybe.of(5);
map(inc, a); // Maybe(6)

var b = Maybe.of(null);

console.log(map(inc, b)); // Maybe(null)
var Maybe = function(x) {
  this.__value = x;
}

Maybe.of = function(x) {
  return new Maybe(x);
}

Maybe.prototype.isNothing = function() {
  return (this.__value === null || 
          this.__value === undefined);
}

Maybe.prototype.map = function(f) {
  return this.isNothing() ? 
         Maybe.of(null) : Maybe.of(f(this.__value));
}

Пример:

var jsify = function(str) { return str + ".js" }
var beerify = function(str) { return "Beer" + str }
var a = Maybe.of("");

compose(map(capitalize), 
        map(beerify), 
        map(jsify)) (a);
Maybe.of("BEER.JS");

Ако функцията може да се "счупи", връща Maybe.of(null)

var safeHead = function(xs) {
  return Maybe.of(xs[0]);
};
var a = [1, 2, 3];

console.log(compose(inc, safeHead)(a));
// Maybe.of(2)

Ако функцията може да се "счупи", връща Maybe.of(null)

var safeHead = function(xs) {
  return Maybe.of(xs[0]);
};
var b = [];

console.log(compose(inc, safeHead)(b));
// Maybe.of(null)
getDom :: String -> Maybe DOMElement
addText :: String -> DOMElement -> DOMElement
var element1 = getDom("Something missing");
var element2 = getDom("#someId");

map(addText("Beer.JS!"), element1);
// Maybe.of(null);

map(addText("Beer.JS!"), element2);
// Maybe.of(DOMElement("Beer.JS!"))

Енкапсулираме Null Checking

Ако се появи null от някаква сметка:

var nullCreator = function() { return null; }
var a = Maybe.of("");

compose(map(capitalize),
        map(nullCreator), 
        map(beerify), 
        map(jsify))(a);
Maybe.of(null);

Оригиналната идея

data Maybe a = Just a | Nothing
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x

Материали

Made with Slides.com