Это монада?
Я пытаюсь понять концепцию монадов, и я хочу знать, является ли этот код реализацией этой концепции (в JavaScript).
У меня есть функция M, которая возвращает новый объект, который установил метод, который создает метод обёртки
var foo = M().set('getX', function() {
return this.x;
}).set('setX', function(x) {
this.x = x;
}).set('addX', function(x) {
this.x += x;
});
А потом я могу связать метод foo
foo.setX(10).addX(20).addX(30).getX()
вернет 60
и то же самое, если у меня есть объект с методами и вызовите M с этим объектом.
var foo = {
x: 10,
add: function(x) {
this.x += x;
}
};
M(foo).add(10).add(20).add(30).x
вернет 70
Функции обернуты внутри объекта M, поэтому этот контекст внутри метода всегда является объектом M.
f = M({x: 20}).set('getX', function() {
return this.x;
}).set('addX', function(x) {
this.x += x;
}).addX(10).getX
так что f - это функция с контекстом объекта, обернутого M - если я вызываю f()
, он вернет 30.
Я правильно понимаю это? Является ли монада монахом?
EDIT изменен код на github https://github.com/jcubic/monadic
Ответы
Ответ 1
Это моноидный шаблон. Каждая операция обновления состояния, такая как .setX(10)
, .addX(20)
и т.д., Представляет собой вычисление, которое преобразует один объект. (Чтобы быть синтаксически корректным, вам нужно написать его как однопараметрическую функцию function(x) {x.addX(20);}
, но я думаю, что яснее, если я использую короткую форму.)
Две вещи делают это моноидом. Во-первых, существует элемент идентификации: .addX(0)
ничего не делает для своего объекта. Во-вторых, любые две операции могут быть объединены. Например, .setX(10).addX(20)
- это также вычисление, которое преобразует один объект.
Это не монада. Вычисления, поддерживаемые вашими методами, ограничены написанием и обновлением this.x
. (.getX()
не является членом моноида, потому что после него вы ничего не можете связать). Например, с монадой вы можете иметь одного члена цепочки операций, чтобы выполнить if-then-else, чтобы решить, что будет дальше в цепочке. Ваши методы не могут этого сделать.
Ответ 2
Отменяемость; к моему пониманию, то, что вы написали, ближе к прикладному функтору, чем монада или моноид.
Опять же, насколько я понимаю, моноид - это группа (в абстрактном алгебраическом смысле), замкнутая под одной операцией, отображающая один тип для себя. Если бы вы реализовали только add
, вы могли бы сказать, что ваша цепочка прототипов реализовала моноид. Но даже тогда вам придется вручную указывать сокращение вручную, как двоичную операцию, между каждым и каждым аргументом, например:
M({x:0}).add(1).add(2)...add(100) === 1050; // or _.reduce([1..100],add)
Но так как вы привязали неопределенное количество функций к типу (M
), которые все умеют "разворачивать" этот тип, применяют намеченную функцию, затем восстанавливают "обертку" при выходе, тогда у вас есть своего рода прикладной функтор.
Если вы нашли какой-то способ для компоновки областей всех функций, работающих на M
, то вы по-прежнему будете ближе к монадической реализации:
var bigOpFromLittleOps =
M({x:0}) .bind(function(x0){
return Madd(1) .bind(function(x1){
return Madd(2) .bind(function(x2){
...
return Madd(100) .bind(function(x100){
return Mreturn(x100);
}); ... });});})() === 1050; // Overkill
Такие реализации сложны, но дают вам возможность срезать и нарезать их на маленькие кусочки и/или составлять более крупные из меньших.