2013-11-24 4 views
2

Как я узнал о монадах после чтения десятков уроков Я пытаюсь реализовать шаблон в JavaScript. Я использую LiveScript и Prelude, чтобы перевести некоторые примеры монахов Хаскелла.Monad pattern on receiver

Итак, монада, которую я пытаюсь реализовать в качестве упражнения, является монадой списка. Я писал, в LiveScript:

List = do -> 
    # unit :: a -> ma 
    unit = (a) -> [a] 
    # bind :: ma -> (a -> mb) -> mb 
    bind = (ma, f) --> concat (map f) ma 
    # lift :: (a -> b) -> ma -> mb 
    lift = (f, ma) --> bind ma, (a) -> unit f a 

    {unit, bind, lift} 

add1 = (x) -> x+1 

let {unit, bind} = List 
    x <- bind [1] 
    y <- bind [2] 
    z <- bind [3] 
    unit add1 x+y+z #=> [7] 

(List.lift add1) [1 2 3] #=> [2 3 4] 

Синтаксис LiveScript к функциям гнезда на тот же уровень отступа очень удобно, но это приводит к обратному вызову ад JavaScript, очевидно:

List = function(){ 
    var unit, bind, lift; 
    unit = function(a){ 
    return [a]; 
    }; 
    bind = curry$(function(ma, f){ 
    return concat(map(f)(ma)); 
    }); 
    lift = curry$(function(f, ma){ 
    return bind(ma, function(a){ 
     return unit(f(a)); 
    }); 
    }); 
    return { unit: unit, bind: bind, lift: lift }; 
}(); 
add1 = function(x){ 
    return x + 1; 
}; 
(function(arg$){ 
    var unit, bind; 
    unit = arg$.unit, bind = arg$.bind; 
    bind([1], function(x){ 
    return bind([2], function(y){ 
     return bind([3], function(z){ 
     return unit(add1(x + y + z)); 
     }); 
    }); 
    }); 
}.call(this, List)); 
List.lift(add1)([1, 2, 3]); 

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

List([1]).bind([2]).bind([3]).do(function(x,y,z){ x+y+z }); 

После просмотра this video где Крокфорд объясняет монады в J avaScript (code), я понимаю, что представленный им MONAD представляет собой объект с методами, которые также могут быть реализованы с помощью прототипов. Метод unit является конструктором, а bind - это метод экземпляра, который выполняет функцию по значению с данными аргументами. Затем lift добавляет новый метод прототипу, который запускает заданную функцию по монадическому значению.

Но, это настоящая монада или монадический узор like jQuery? Я сомневаюсь в этом конкретном объяснении монад, потому что нет последовательности вычислений, метод bind запускает функцию сразу и возвращает новый экземпляр «монады», это не композиция, как в монаде, который я реализовал в LiveScript, который был основанный на примерах Haskell.

Так что мой вопрос:

  1. Является ли моя реализация монады правильно?
  2. Crockford's implementation от monad correct?
+0

Я не уверен, что «Список ([1]). Bind ([2]). Bind ([3]). Do (function (x, y, z) {x + y + z}); 'должен делать. 'bind' обычно принимает функцию (которая создает список)? – Bergi

+0

Я думаю, что это часть, которую я еще не совсем понимаю, - как заставить монаду работать на приемнике, но по-прежнему использовать функцию композиции. Как это должно было бы выглядеть с моим примером тогда? – elclanrs

+0

У вас есть три экземпляра монады, так что это будет нелегко. Эти обратные вызовы LiveScript, так же, как и Haskells, представляют собой просто синтаксический сахар для * необходимого * лифтинга обратного вызова. Вам еще нужно написать «List ([1]). Bind ((x) -> List ([2]). Bind ((y) -> List ([3]). Bind ((z) -> List .unit (х + у + г)))) '. Разумеется, для этого вы могли бы реализовать некоторые функции сахара, в этом случае ['lift3'] (http://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Monad.html#v:liftM3). – Bergi

ответ

3

Является ли моя реализация монады правильной?

Да, ваши unit и bind функции выполняют то, что ожидается от Монады Список.

Однако вторая строка, List([1]).bind([2]).bind([3]).do(function(x,y,z){ x+y+z }); не похожа на монаду.

Эти обратные вызовы LiveScript, так же как и обозначение Haskell, являются просто синтаксическим сахаром для нужного lift обратного ад. Вы по-прежнему нужно будет написать:

List([1]).bind((x)->List([2]).bind((y)->List([3]).bind((z)->List.unit(x+y+z))))‌ 

Если выражается через bind он всегда будет «беспорядок». Для того, чтобы сделать его лучше (и более производительный), вы бы использовать список понимание:

concat(for x in [1] for y in [2] for z in [3]: x+y+z) 

Еще одна идея: из-за слабого в JavaScript печатал должна быть возможность реализовать истинный общий (рекурсивный?) liftN функция с переменным числом аргументов, если вы хотите:

function lift(n, fn) { 
    var argsPos = 2; 
    if (typeof n != "number") { 
     fn = n; 
     n = fn.length; 
     argsPos--; 
    } 
    var args = [].slice.call(arguments, argsPos); 
    if (n < args.length) // curry 
     return function(){ 
      return lift.apply(null, [n, fn].concat(args, [].slice.call(arguments))); 
     } 
    return (function bindr(bound, args) 
     if (!args.length) 
      return unit(fn.apply(null, bound)); 
     return bind(args[0], function(a) { 
      return bindr([x].concat(bound), args.slice(1)); 
     }); 
    })([], args); 
} 

Если вы хотите использовать более объектно-ориентированной модели, то можно сопоставить отдельные комбинации аргумента кортежей, которые можно применить в конце концов:

List([1]).nest(List([2])).nest(List([3])).do(function(x,y,z){ x+y+z }) 
// where 
Monad.prototype.map = function(fn) { 
    var unit = this.constructor; // ??? 
    return this.bind(function(x) 
     return unit(fn(x)); 
    }); 
}; 
Monad.prototype.nest = function(m2) { 
    return this.map(function(x) { 
     return m2.map(function(y) 
      return [x, y]; // tuple 
     }); 
    }); 
}); 
Monad.prototype.do = function(fn, n) { 
    function flatten(n, t) { 
     return n<=1 ? [t] : flatten(n-1, t[0]).concat([t[1]]); 
    } 
    return this.map(function(ts) { 
     return fn.apply(null, flatten(n || fn.length, ts)); 
    }); 
}; 

Является ли реализация Crockford монадой правильной?

Возможно. Он реализует монадию idendity, но код выглядит так, как будто он хочет распространить его на другие монады, перезаписав метод bind, который может не работать легко для всех монадов.

+0

Спасибо за обновление, выглядит хорошо, попробуем. – elclanrs