javascript - 如果函数和参数均为 “wrapped”,如何使用 lift

标签 javascript functional-programming

我一直在尝试基于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 调用。

问题

将一个 mapN - 1 调用链接到 apliftN 方式仅在我使用时才有效简单的函数。然而,在我的应用程序中,我经常处理本身包含在订阅内容中的函数!例如:

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 handles Apply f => (x -> y -> z) -> f x -> f y -> f z, what is the theory/name behind Apply 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/

相关文章:

javascript - 函数调用的父函数是否像分组运算符一样?

javascript - 尝试修复元素的高度,以便在悬停时正确突出显示

javascript - Three.js:无法设置最大和最小缩放级别

clojure - 种植我的 Clojure 树

f# - F#中的异步屏障

clojure - Clojure 中的 def 和 defonce 有什么区别?

ios - RxSwift 过滤器变量数组

javascript - jquery如何发布到php,同步

javascript - jquery Accordion 在页面加载时默认折叠

javascript - 如何防止在 ClickFunnels 上提交表单