首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我可以将函数传递给一个提升的R.divide?

为什么我可以将函数传递给一个提升的R.divide?
EN

Stack Overflow用户
提问于 2016-09-17 10:47:00
回答 2查看 296关注 0票数 7

鉴于以下情况:

代码语言:javascript
复制
var average = R.lift(R.divide)(R.sum, R.length)

为什么这作为average的无点实现工作呢?我不明白为什么当R.sumR.length是函数时,我可以传递它们,因此,与下面的示例不同,我不能在函数R.sumR.length上映射已解除的R.divide

代码语言:javascript
复制
var sum3 = R.curry(function(a, b, c) {return a + b + c;});
R.lift(sum3)(xs)(ys)(zs)

在上述情况下,xsyszs中的值被求和在一个不确定的上下文中,在这种情况下,提升函数被应用于给定的计算上下文中的值。

进一步说明,我理解应用一个提升函数就像在每个参数之前使用R.ap一样。这两行的计算结果相同:

代码语言:javascript
复制
R.ap(R.ap(R.ap([tern], [1, 2, 3]), [2, 4, 6]), [3, 6, 8])
R.lift(tern)([1, 2, 3], [2, 4, 6], [3, 6, 8])

检查文件上写着:

“解除”一个大于1的函数,以便它可以“映射”满足FantasyLand应用规范的列表、函数或其他对象。

至少对我来说,这似乎不是一个非常有用的描述。我正在尝试建立一个关于lift使用的直觉。我希望有人能提供这个。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-17 13:10:19

第一件很酷的事情是a -> b可以支持map。是的,函数是函子!

让我们考虑一下map的类型

代码语言:javascript
复制
map :: Functor f => (b -> c) -> f b -> f c

让我们将Functor f => f替换为Array,给出一个具体的类型:

代码语言:javascript
复制
map :: (b -> c) -> Array b -> Array c

让我们将Functor f => f替换为Maybe,这次:

代码语言:javascript
复制
map :: (b -> c) -> Maybe b -> Maybe c

两者之间的关系是明确的。让我们将Functor f => f替换为Either a,以测试二进制类型:

代码语言:javascript
复制
map :: (b -> c) -> Either a b -> Either a c

我们通常将从ab的函数类型表示为a -> b,但这实际上只是Function a b的糖。让我们使用长表单并将上面签名中的Either替换为Function

代码语言:javascript
复制
map :: (b -> c) -> Function a b -> Function a c

因此,对函数的映射提供了一个函数,它将b -> c函数应用于原始函数的返回值。我们可以使用a -> b糖重写签名:

代码语言:javascript
复制
map :: (b -> c) -> (a -> b) -> (a -> c)

注意到什么了吗?compose的类型是什么?

代码语言:javascript
复制
compose :: (b -> c) -> (a -> b) -> a -> c

因此,compose只是专门针对函数类型的map

第二件很酷的事情是,a -> b可以支持ap。函数也是应用函子!这些都被称为应用在梦幻之地的规范。

让我们考虑一下ap的类型

代码语言:javascript
复制
ap :: Apply f => f (b -> c) -> f b -> f c

让我们将Apply f => f替换为Array

代码语言:javascript
复制
ap :: Array (b -> c) -> Array b -> Array c

现在,用Either a

代码语言:javascript
复制
ap :: Either a (b -> c) -> Either a b -> Either a c

现在,用Function a

代码语言:javascript
复制
ap :: Function a (b -> c) -> Function a b -> Function a c

Function a (b -> c)是什么?这有点让人困惑,因为我们将这两种样式混合在一起,但它是一个函数,它接受a类型的值,并从b返回一个函数到c。让我们使用a -> b样式重写:

代码语言:javascript
复制
ap :: (a -> b -> c) -> (a -> b) -> (a -> c)

任何支持mapap的类型都可以“取消”。让我们来看看lift2

代码语言:javascript
复制
lift2 :: Apply f => (b -> c -> d) -> f b -> f c -> f d

请记住,Function a满足应用的要求,所以我们可以用Function a代替Apply f => f

代码语言:javascript
复制
lift2 :: (b -> c -> d) -> Function a b -> Function a c -> Function a d

它写得更清楚:

代码语言:javascript
复制
lift2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)

让我们重新讨论您的初始表达式:

代码语言:javascript
复制
//    average :: Number -> Number
const average = lift2(divide, sum, length);

average([6, 7, 8])是做什么的?a ([6, 7, 8])被赋予a -> b函数(sum),产生b (21)。a也被赋予a -> c函数(length),产生c (3)。现在我们有了一个b和一个c,我们可以将它们提供给b -> c -> d函数(divide)来生成一个d (7),这是最后的结果。

因此,由于函数类型可以支持mapap,所以我们免费获得converge (通过liftlift2lift3)。实际上,我想从Ramda中删除converge,因为这是不必要的。

注意,我有意避免在这个答案中使用R.lift。它有一个无意义的类型签名和复杂的实现,因为它决定支持任何的功能。圣所特有的提升功能,另一方面,有明确的类型签名和琐碎的实现。

票数 13
EN

Stack Overflow用户

发布于 2016-12-31 19:18:39

由于我很难理解同样的问题,所以我决定从Ramda的源代码中浏览一下。将来会写一篇关于这件事的博客。同时,我做了一个评论的要点,拉姆达的lift如何一步一步地工作。

来自:https://gist.github.com/philipyoungg/a0ab1efff1a9a4e486802a8fb0145d9e

代码语言:javascript
复制
// Let's make an example function that takes an object and return itself.
// 1. Ramda's lift level
lift(zipObj)(keys, values)({a: 1}) // returns {a: 1}

// this is how lift works in the background
module.exports = _curry2(function liftN(arity, fn) {
  var lifted = curryN(arity, fn);
  return curryN(arity, function() {
    return _reduce(ap, map(lifted, arguments[0]), Array.prototype.slice.call(arguments, 1)); // found it. let's convert no 1 to no 2
  });
});

// 2. Ramda's reduce level
reduce(ap, map(zipObj, keys))([values])
// first argument is the function, second argument is initial value, and the last one is lists of arguments. If you don't understand how reduce works, there's a plenty of resources on the internet

// 3. Ramda's ap level
ap(map(zipObj, keys), values)

// how ap works in the background
module.exports = _curry2(function ap(applicative, fn) {
  return (
    typeof applicative.ap === 'function' ?
      applicative.ap(fn) :
    typeof applicative === 'function' ? // 
      function(x) { return applicative(x)(fn(x)); } : // because the first argument is a function, ap return this.
    // else
      _reduce(function(acc, f) { return _concat(acc, map(f, fn)); }, [], applicative)
  );
});

// 4. Voilà. Here's the final result.
map(zipObj, keys)({a: 1})(values({a: 1}))

// Hope it helps you and everyone else!
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39545916

复制
相关文章

相似问题

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