这是 Javascript 中有效的 monad 转换器吗?
Is this a valid monad transformer in Javascript?
为了更好地理解 monad 转换器,我实现了一个。由于 Javascript 是动态类型的,我不模仿类型或数据构造函数,而是仅声明普通的旧 Javascript 对象,这些对象包含相应的静态函数以形成特定的 monad / transformer。基本思想是将这些方法应用于容器类型中的 value/values。可以说类型和容器是分开的。
Array
s 可以包含任意数量的元素。扩展 Array
s 以便它们实现 monad 接口是微不足道的。 Array
s 也可以表示 maybe
类型的两个变体。一个空的 Array
对应于 nothing
。具有单个元素的 Array
对应于 just(a)
。因此,我将使用 Array
s 作为我的容器类型。请注意,这是一个仅供学习的快速而肮脏的实现:
const array = {
of: x => Array.of(x),
map: f => ftor => ftor.map(f),
ap: ftor => gtor => array.flatten(array.map(f => array.map(f) (gtor)) (ftor)),
flatten: ftor => ftor.reduce((xs, y) => xs.concat(y), []),
chain: mf => ftor => array.flatten(array.map(mf) (ftor))
}
const maybe = {
of: array.of,
empty: () => [],
throw: ftor => { if (ftor.length > 1) throw Error("indeterministic value"); return ftor },
map: f => ftor => maybe.throw(ftor).map(f),
ap: ftor => gtor => maybe.flatten(maybe.map(f => maybe.map(f) (gtor)) (ftor)),
flatten: array.flatten,
chain: mf => ftor => maybe.flatten(maybe.map(mf) (ftor)),
T: M => {
return {
of: x => M.of(maybe.of(x)),
empty: () => M.of(maybe.empty()),
map: f => ftor => M.map(gtor => maybe.map(f) (gtor)) (ftor),
ap: ftor => gtor => M.flatten(M.map(htor => M.map(itor => maybe.ap(htor) (itor)) (gtor)) (ftor)),
flatten: maybe.flatten,
chain: mf => ftor => M.chain(gtor => maybe.chain(mf) (gtor)) (ftor)
};
}
};
现在我将一个 maybe 转换器与 monadic 数组结合起来,得到一个可以处理 array
s of maybe
s 的 monad。
const arraym = maybe.T(array);
const add = x => y => x + y;
const addm = x => y => [x + y];
const arrayOfMaybes = [[1],[],[3]]
当我将 arraym
视为应用函子时,一切都按预期工作:
// yields: [[11],[],[13]] as expected
arraym.ap(arraym.map(add) (arrayOfMaybes)) (arraym.of(10));
但是,当我申请时 chain
出了点问题:
// yields: [11,13] but [[11],[13]] expected
arraym.chain(x => arraym.chain(y => addm(x) (y)) (arrayOfMaybes)) ([[10]])
是这个问题的原因
- 这不是一个有效的 monad 转换器?
- 我申请链条的方式不对?
- 我对结果的预期是错误的?
Is the cause of this problem that the way I apply chain is wrong?
是的。您需要传递 mf
,returns 传递 arraym
,而不是像 addm
那样传递 array
。你可以使用
const addmm = x => y => array.map(maybe.of)(addm(x)(y))
arraym.chain(x => arraym.chain( addmm(x) )(arrayOfMaybes))([[10]])
为此,您还可以考虑为每个 monad 转换器实现 lift
。
为了更好地理解 monad 转换器,我实现了一个。由于 Javascript 是动态类型的,我不模仿类型或数据构造函数,而是仅声明普通的旧 Javascript 对象,这些对象包含相应的静态函数以形成特定的 monad / transformer。基本思想是将这些方法应用于容器类型中的 value/values。可以说类型和容器是分开的。
Array
s 可以包含任意数量的元素。扩展 Array
s 以便它们实现 monad 接口是微不足道的。 Array
s 也可以表示 maybe
类型的两个变体。一个空的 Array
对应于 nothing
。具有单个元素的 Array
对应于 just(a)
。因此,我将使用 Array
s 作为我的容器类型。请注意,这是一个仅供学习的快速而肮脏的实现:
const array = {
of: x => Array.of(x),
map: f => ftor => ftor.map(f),
ap: ftor => gtor => array.flatten(array.map(f => array.map(f) (gtor)) (ftor)),
flatten: ftor => ftor.reduce((xs, y) => xs.concat(y), []),
chain: mf => ftor => array.flatten(array.map(mf) (ftor))
}
const maybe = {
of: array.of,
empty: () => [],
throw: ftor => { if (ftor.length > 1) throw Error("indeterministic value"); return ftor },
map: f => ftor => maybe.throw(ftor).map(f),
ap: ftor => gtor => maybe.flatten(maybe.map(f => maybe.map(f) (gtor)) (ftor)),
flatten: array.flatten,
chain: mf => ftor => maybe.flatten(maybe.map(mf) (ftor)),
T: M => {
return {
of: x => M.of(maybe.of(x)),
empty: () => M.of(maybe.empty()),
map: f => ftor => M.map(gtor => maybe.map(f) (gtor)) (ftor),
ap: ftor => gtor => M.flatten(M.map(htor => M.map(itor => maybe.ap(htor) (itor)) (gtor)) (ftor)),
flatten: maybe.flatten,
chain: mf => ftor => M.chain(gtor => maybe.chain(mf) (gtor)) (ftor)
};
}
};
现在我将一个 maybe 转换器与 monadic 数组结合起来,得到一个可以处理 array
s of maybe
s 的 monad。
const arraym = maybe.T(array);
const add = x => y => x + y;
const addm = x => y => [x + y];
const arrayOfMaybes = [[1],[],[3]]
当我将 arraym
视为应用函子时,一切都按预期工作:
// yields: [[11],[],[13]] as expected
arraym.ap(arraym.map(add) (arrayOfMaybes)) (arraym.of(10));
但是,当我申请时 chain
出了点问题:
// yields: [11,13] but [[11],[13]] expected
arraym.chain(x => arraym.chain(y => addm(x) (y)) (arrayOfMaybes)) ([[10]])
是这个问题的原因
- 这不是一个有效的 monad 转换器?
- 我申请链条的方式不对?
- 我对结果的预期是错误的?
Is the cause of this problem that the way I apply chain is wrong?
是的。您需要传递 mf
,returns 传递 arraym
,而不是像 addm
那样传递 array
。你可以使用
const addmm = x => y => array.map(maybe.of)(addm(x)(y))
arraym.chain(x => arraym.chain( addmm(x) )(arrayOfMaybes))([[10]])
为此,您还可以考虑为每个 monad 转换器实现 lift
。