var average = R.lift(R.divide)(R.sum,R.length)
为什么这可以作为平均点的免费实现?我不明白为什么我可以在它们是函数时传递R.sum和R.length,因此,我不能将提升的R.divide映射到函数R.sum和R.length上,这与以下示例不同:
var sum3 = R.curry(function(a,b,c) {return a + b + c;}); R.lift(sum3)(xs)(ys)(zs)
在上述情况下,xs,ys和zs中的值在非确定性上下文中求和,在这种情况下,将提升函数应用于给定计算上下文中的值.
进一步说明,我理解应用提升函数就像连续使用R.ap到每个参数.两行都评估相同的输出:
R.ap(R.ap(R.ap([tern],[1,2,3]),[2,4,6]),[3,6,8]) R.lift(tern)([1,3],6],8])
检查文档说:
“lifts” a function of arity > 1 so that it may “map over” a list,Function or other object that satisfies the FantasyLand Apply spec.
至少对我来说,这似乎不是一个非常有用的描述.我正试图建立一个关于电梯使用的直觉.我希望有人可以提供.
解决方法
map
.是的,功能是仿函数!
让我们考虑一下地图的类型:
map :: Functor f => (b -> c) -> f b -> f c
让我们替换Functor f => f with Array给我们一个具体的类型:
map :: (b -> c) -> Array b -> Array c
让我们替换Functor f =>这个时候可能还有:
map :: (b -> c) -> Maybe b -> Maybe c
相关性很明显.让我们替换Functor f => f使用a,a来测试二进制类型:
map :: (b -> c) -> Either a b -> Either a c
我们经常将a到b的函数类型表示为 – > b,但这真的只是功能a b的糖.让我们使用长格式并将上面的签名中的Either替换为Function:
map :: (b -> c) -> Function a b -> Function a c
因此,映射函数给我们一个函数,它将应用b – > c函数为原始函数的返回值.我们可以使用a – >重写签名. b糖:
map :: (b -> c) -> (a -> b) -> (a -> c)
注意什么? compose
的类型是什么?
compose :: (b -> c) -> (a -> b) -> a -> c
所以compose只是专门用于Function类型的map!
第二个很酷的事情是a – > b可以支持ap
.函数也是应用函子!这些被称为Fantasy Land规范中的Apply.
让我们考虑一下ap的类型:
ap :: Apply f => f (b -> c) -> f b -> f c
让我们替换Apply f => f与数组:
ap :: Array (b -> c) -> Array b -> Array c
现在,使用Either a:
ap :: Either a (b -> c) -> Either a b -> Either a c
现在,使用功能a:
ap :: Function a (b -> c) -> Function a b -> Function a c
什么是函数a(b – > c)?这有点令人困惑,因为我们混合了两种样式,但它是一个函数,它接受一个类型a的值并返回一个从b到c的函数.让我们用a – >重写b风格:
ap :: (a -> b -> c) -> (a -> b) -> (a -> c)
任何支持map和ap的类型都可以“解除”.我们来看看lift2
:
lift2 :: Apply f => (b -> c -> d) -> f b -> f c -> f d
请记住,函数a满足Apply的要求,因此我们可以替换Apply f => f与函数a:
lift2 :: (b -> c -> d) -> Function a b -> Function a c -> Function a d
哪个更清楚:
lift2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
让我们重新审视您的初始表达:
// average :: Number -> Number const average = lift2(divide,sum,length);
平均值([6,7,8])有什么作用? a([6,8])给予a – > b函数(sum),产生b(21). a也给a – > c函数(长度),产生c(3).现在我们有了b和c,我们可以将它们提供给b – > c – > d函数(除)产生d(7),这是最终结果.
因此,因为Function类型可以支持map和ap,所以我们可以免费收敛(通过lift
,lift2
和lift3
).我实际上想从Ramda中移除converge,因为它没有必要.
请注意,我有意避免在此答案中使用R.lift
.由于决定支持任何arity的功能,它具有无意义的类型签名和复杂的实现.另一方面,Sanctuary特定的提升功能具有清晰的类型签名和简单的实现.