我一直在尝试基于Javascript Functor, Applicative, Monads in pictures实现Apply/Applicative类型以及博客系列Fantas, Eel, and Specification .
我认为我取得了很好的进展,但我遇到了一个在任何文章中都找不到的案例。
TL;DR 问题
如果lift2
是
f -> A.of(x) -> A.of(y) -> A.of(f (x) (y)) -or- A(y).ap(A(x).map(f))
背后的理论/名称/类型是什么
A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y)) -or- A(y).ap(A(x).ap(A(f)))
<小时/>
简介
我正在与 knockout.js 合作,这给了我可观察的值。我尝试以一种功能性的方式使用它们,并在需要时扩展它们。
首先,我实现了 map
来让自己成为 functor :
ko.subscribable.fn.map = function(f) {
// note: calling without arguments is knockout's
// way of "unwrapping"
return ko.pureComputed(
() => f(this())
);
}
这允许我做这样的事情:
// "Pure" part describing my app
const myValue = ko.observable(2);
const doubleThat = myValue.map(x => x * 2);
// In- and output (IO?)
doubleThat.subscribe(console.log);
myValue(3); // Logs 6
然后,我遇到了使用带有多个参数的函数的问题。例如:
const filter = (pred, xs) => xs.filter(pred);
我通过实现 ap
并柯里化(Currying)我的函数解决了我的问题:
ko.subscribable.fn.ap = function(sf) {
return ko.pureComputed(
() => sf () (this())
);
};
const filter = pred => xs => xs.filter(pred);
完成这些更改后,我可以执行以下操作:
const odd = x => x % 2 === 1;
const myPred = ko.observable(odd);
const myValues = ko.observable([ 1, 2, 3 ]);
const myFilter = myPred.map(filter);
const myResult = myValues.ap(filter); // S([ 1, 3 ])
lift2
的定义给了我另一种写同样东西的方式。
const myResult = lift2 (filter) (myPred) (myResult)
到目前为止,一切都很好。如果临时结果可重用,我可以使用点调用;如果我只关心最终结果,则可以使用 liftN
调用。
问题
将一个 map
与 N - 1
调用链接到 ap
的 liftN
方式仅在我使用时才有效简单的函数。然而,在我的应用程序中,我经常处理本身包含在订阅内容中的函数!例如:
const sum = x => y => x + y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(2);
const v2 = ko.observable(3);
我的尝试
链接有效,但很快就会变得很难理解。
// Ugly...
const myResult = v2.ap(v1.ap(mathStrategy)); // S(5)
我可以使用 liftN
,但前提是我确保我的第一个函数是 id
。
// Also ugly...
const id = x => x;
const myResultL = lift3 (id) (mathStrategy) (v1) (v2); // S(5)
我的问题
- 如果
lift2
处理f -> A.of(x) -> A.of(y) -> A.of(f (x) (y))
,A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y)) 背后的理论/名称/类型是什么
- 如果这样的事情并不真正“存在”,那么编写一个可以随时解开
A(f)
的ap
实现是否可以? (即f => ko.unwrap (f) (x)
)
代码示例
Object.assign(ko.subscribable, {
of: function(x) {
return ko.pureComputed(() => x)
}
});
Object.assign(ko.subscribable.fn, {
map: function(f) {
return ko.pureComputed(() => f(this()));
},
ap: function(sf) {
return ko.pureComputed(() => sf () (this()));
},
toString: function() {
return `S(${JSON.stringify(this())})`;
}
});
// Example code:
const sum = x => y => x + y;
const mult = x => y => x * y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(1);
const v2 = ko.observable(3);
const result = v2.ap(v1.ap(mathStrategy));
console.log(result); // S(4)
v1(2);
mathStrategy(mult);
console.log(result); // S(6)
.as-console-wrapper { min-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<子>
注意:这与a question I asked earlier有关。 。当时,我专注于懒惰地解开 A(x)
和 A(y)
,现在我已经放弃了😉。
最佳答案
我认为您想问的问题(带有正确的类型符号)是
If
lift2
handlesApply f => (x -> y -> z) -> f x -> f y -> f z
, what is the theory/name behindApply f => f (x -> y -> z) -> f x -> f y -> f z
?
这样的函数很少见,因为每个人都只使用 ap
或各自的中缀运算符,就像在 Haskell 中一样
let az = af <*> ax <*> ay
但是,我在互联网上看到一些两个引用它被称为ap2
[1] [2] ,这是有道理的,因为它类似于 lift
变成 lift2
。
您可以将此辅助函数编写为
const ap2 = af => ax => ay => ap(ap(af, ax), ay)
或者像你已经做的那样
const ap2 = lift3(id)
关于javascript - 如果函数和参数均为 “wrapped”,如何使用 lift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58122339/