首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在JavaScript中实现monads

在JavaScript中实现monads
EN

Stack Overflow用户
提问于 2013-12-22 19:47:58
回答 4查看 2.9K关注 0票数 17

现在

node.js

支持

ECMAScript调和生成器

我们可以简洁地编写一元代码

Haskell中的块:

代码语言:javascript
复制
function monad(unit, bind) {
    return function (f) {
        return function () {
            var g = f.apply(this, arguments);

            return typeOf(g) === "Generator" ? send() : unit(g);

            function send(value) {
                var result = g.next(value);
                if (result.done) return unit(result.value);
                else return bind(result.value, send);
            }
        };
    };
}

function typeOf(value) {
    return Object.prototype.toString.call(value).slice(8, -1);
}

在上面的代码中

是一个函数,可用于创建

确定性

monads,如:

代码语言:javascript
复制
var maybe = monad(function (a) {
    return {just: a};
}, function (m, f) {
    return m === null ? null : f(m.just);
});

您现在可以使用

如下所示:

代码语言:javascript
复制
var readZip = maybe(function * (a, b) {
    var a = yield readList(a);
    var b = yield readList(b);
    return _.zip(a, b);
});

上述函数

获取两个字符串,将它们转换为列表,然后压缩它们。如果出现错误,它会立即返回

..。它取决于以下函数:

代码语言:javascript
复制
function readList(string) {
    try {
        var value = JSON.parse(string);
        return value instanceof Array ? {just: value} : null;
    } catch (error) {
        return null;
    }
}

我们测试它以检查它是否如预期的那样工作:

代码语言:javascript
复制
console.log(readZip('[1,2,3,4]', '["a","b"]')); // [[1,"a"],[2,"b"],[3,"c"]]
console.log(readZip('hello', '["a","b"]'));     // null
console.log(readZip('[1,2,3,4]', 'world'));     // null

类似地,我们可以创建任何其他确定性单子。例如,我最喜欢的

monad:

代码语言:javascript
复制
var cont = monad(function (a) {
    return function (k) {
        return k(a);
    };
}, function (m, k) {
    return function (c) {
        return m(function (a) {
            return k(a)(c);
        });
    };
});

现在我们可以使用

要简洁地创建连续传递样式的函数,请执行以下操作:

代码语言:javascript
复制
var fib = cont(function * (n) {
    switch (n) {
    case 0: return 0;
    case 1: return 1;
    default:
        var x = yield fib(n - 1);
        var y = yield fib(n - 2);
        return x + y;
    }
});

您可以使用

函数如下:

代码语言:javascript
复制
fib(10)(function (a) { console.log(a); }); // 55

不幸的是

仅适用于确定性单体。它不适用于像

monad,因为您只能从特定位置恢复生成器一次。

所以我的问题是:有没有其他方法来实现像

monad在JavaScript中简明扼要?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2019-06-29 13:42:05

所以我的问题是:有没有其他方法来实现像

monad在JavaScript中简明扼要?

是的,您可以使用生成器在JavaScript中简洁地实现像list monad这样的非确定性monad。

免疫原

..。但是,由于JavaScript中的生成器不能多次从特定位置恢复,因此您必须通过创建和重放多个生成器来模拟此行为。这种解决方案有几个缺点。

它的效率很低,因为需要创建和重放多个生成器,导致时间复杂度的二次增长。

它只适用于纯monads和纯计算,因为需要创建和重放多个生成器。因此,副作用将被错误地多次执行。

我们需要不可变的生成器来创建不确定的monad,比如list monad。不可变生成器可以多次从特定位置恢复。不幸的是,JavaScript本身并不支持不可变的生成器。但是,我们可以通过创建和重放多个可变生成器来模拟它。因此,让我们看看如何创建一个不可变的生成器。

我们需要解决的第一个问题是将可变生成器重放到特定点。我们使用一种称为再生器的特殊函数类来实现这一点。再生器是任何返回可变生成器的函数。这类函数的最简单示例是

..。因此,每个生成器函数都是再生器,但并不是每个再生器都是生成器函数。您可以使用以下函数通过推进旧的再生器来创建新的再生器。

代码语言:javascript
复制
// type Regenerator = Arguments -> MutableGenerator

// next :: (Regenerator, Arguments) -> Regenerator
const next = (regen, ...args) => data => {
    const gen = regen(...args);
    return gen.next(data), gen;
};

The The The

函数可用于将再生器推进到特定点。例如,考虑下面的代码片段。

代码语言:javascript
复制
const next = (regen, ...args) => data => {
    const gen = regen(...args);
    return gen.next(data), gen;
};

const regen1  = next(regen0, 1, 2, 3);
const regen2  = next(regen1, undefined); // first value of mutable generator ignored
const regen3  = next(regen2, 10);

const gen1 = regen3(20);
const gen2 = regen3(30);

const result1 = gen1.next(40).value; // 10 + 20 + 40
const result2 = gen2.next(50).value; // 10 + 30 + 50

console.log(result1, result2);

function* regen0(a, b, c) {
    const x = yield a;
    const y = yield b;
    const z = yield c;
    return x + y + z;
}

如您所见,我们可以使用

函数或将重新生成器应用于某个值以获得可变生成器。现在我们有能力将可变生成器重放到特定点,我们可以创建不可变生成器,如下所示。

代码语言:javascript
复制
// immutagen :: Regenerator -> Arguments -> ImmutableGenerator
const immutagen = regen => (...args) => function loop(regen) {
    return (gen, data) => {
        const {value, done} = gen.next(data);
        if (done) return {value, next: null};

        let replay = false;
        const recur = loop(next(regen, data));
        return {value, next: value => {
            if (replay) return recur(regen(data), value);
            replay = true; return recur(gen, value);
        }};
    };
}(next(regen, ...args))(regen(...args));

The The The

函数可以用来创建不可变的生成器函数,我们可以调用它来生成不可变的生成器。下面是一个关于如何创建和使用不可变生成器的示例。

代码语言:javascript
复制
const next = (regen, ...args) => data => {
    const gen = regen(...args);
    return gen.next(data), gen;
};

const immutagen = regen => (...args) => function loop(regen) {
    return (gen, data) => {
        const {value, done} = gen.next(data);
        if (done) return {value, next: null};

        let replay = false;
        const recur = loop(next(regen, data));
        return {value, next: value => {
            if (replay) return recur(regen(data), value);
            replay = true; return recur(gen, value);
        }};
    };
}(next(regen, ...args))(regen(...args));

const foo = immutagen(function* (a, b, c) {
    const x = yield a;
    const y = yield b;
    const z = yield c;
    return x + y + z;
});

const bar = foo(1, 2, 3).next(10).next(20);

const result1 = bar.next(30).value; // 10 + 20 + 30
const result2 = bar.next(40).value; // 10 + 20 + 40

console.log(result1, result2);

最后,现在我们有了不可变的生成器,我们可以更简洁地实现像list monad这样的非确定性monad,如下所示:

代码语言:javascript
复制
const next = (regen, ...args) => data => {
    const gen = regen(...args);
    return gen.next(data), gen;
};

const immutagen = regen => (...args) => function loop(regen) {
    return (gen, data) => {
        const {value, done} = gen.next(data);
        if (done) return {value, next: null};

        let replay = false;
        const recur = loop(next(regen, data));
        return {value, next: value => {
            if (replay) return recur(regen(data), value);
            replay = true; return recur(gen, value);
        }};
    };
}(next(regen, ...args))(regen(...args));

const monad = bind => regen => (...args) => function loop({value, next}) {
    return next ? bind(value, val => loop(next(val))) : value;
}(immutagen(regen)(...args));

const flatMap = (array, callback) => array.flatMap(callback);

const list = monad(flatMap);

const foo = list(function* (xs, ys) {
    const x = yield xs;
    const y = yield ys;
    return [x * y];
});

console.log(foo([1, 2, 3], [4, 5, 6]));

请注意,

函数只需要

..。它不需要

..。

票数 3
EN

Stack Overflow用户

发布于 2013-12-22 22:30:54

所以我的问题是:有没有其他方法可以在JavaScript中简洁地实现像list monad这样的非确定性monad?

我建议这个monad实现,我将其应用于各种monad。

这里

代码语言:javascript
复制
var extend = function(a, b) {
  for (var i in b)
    a[i] = b[i];
  return a;
};

// Chain a new `this`
var fluent = function(f) {
  return function() {
    var clone = extend(Object.create(null), this);
    f.apply(clone, arguments);
    return clone;
  };
};

var toArray = function(x) {
  return Array.prototype.slice.call(x);
};

var List = {
  unit: fluent(function() {
    this.x = toArray(arguments);
  }),
  bind: function(f) {
    var fx = this.x.map(f.bind(this));
    var a = fx[0];
    for (var i=1; i [true, true, true]

// lift
var result = List.unit(1,2).bind(List.lift(add1)); //=> [2,3]

console.log(result.valueOf());

// do
var result = List.unit(1,2).bind(function(x) {
  return this.unit(3,4).bind(function(y) {
    return this.unit(x + y);
  });
});

console.log(result.valueOf()); //=> [4,5,5,6]

显然,"do“语法会导致回调地狱,但在

LiveScript

你可以减轻痛苦:

代码语言:javascript
复制
result = do
  x <- List.unit 1 2 .bind
  y <- @unit 3 4 .bind
  @unit x + y

你也可以给你的

创造性的方法:

代码语言:javascript
复制
result = do
  x <- List.unit 1 2 .\>=
  y <- @unit 3 4 .\>=
  @unit x + y
票数 7
EN

Stack Overflow用户

发布于 2015-08-10 17:35:09

似乎有一种很好的方式来实现像这样的list monad:

代码语言:javascript
复制
function* unit(value) {
    yield value;
}
function* bind(list, transform) {
    for (var item of list) {
        yield* transform(item);
    }
}
var result = bind(['a', 'b', 'c'], function (element) {
    return bind([1, 2, 3], function* (element2) {
        yield element + element2;
    });
});
for (var item of result) {
    console.log(item);
}

基于

https://curiosity-driven.org/monads-in-javascript#list

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20729050

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档