2016-09-17 14 views
6

는 다음을 감안할 때? 그들이 기능을 때 나는 R.sumR.length를 전달할 수 있습니다 왜 이해가 안 돼요 때문에, 나는 다음 예에서와 달리 해제 기능 R.sum 이상 R.divideR.length 매핑 할 수 없습니다 : 위의 경우어떻게 함수를 해제 된 R.divide에 전달할 수 있습니까?</p> <pre><code>var average = R.lift(R.divide)(R.sum, R.length) </code></pre> <p>방법이 <code>average</code>의 pointfree 구현으로 작동 제공 :

var sum3 = R.curry(function(a, b, c) {return a + b + c;}); 
R.lift(sum3)(xs)(ys)(zs) 

을 값 xs, yszs은 비 결정적 컨텍스트에서 합계되며,이 경우 리프팅 함수는 주어진 계산 컨텍스트의 값에 적용됩니다.

더 자세히 살펴보면, 해제 된 함수를 적용하면 R.ap을 각 인수에 연속적으로 사용하는 것과 같습니다.

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 Apply 스펙을 만족하는 오브젝트.

그리고 그것은 나를 위해 매우 유용한 설명처럼 보이지 않습니다. 나는 lift의 사용과 관련하여 직관을 구축하려고합니다. 누군가가 제공 할 수 있기를 바랍니다.

답변

13

첫 번째 멋진 점은 a -> bmap을 지원할 수 있다는 것입니다. 예, 함수은 펑터입니다!

의이 map의 유형을 살펴 보자 :

map :: Functor f => (b -> c) -> f b -> f c 

의 우리에게 구체적인 유형 줄 ArrayFunctor f => f을 교체하자

map :: (b -> c) -> Maybe b -> Maybe c 
:

map :: (b -> c) -> Array b -> Array c 

이의이 Maybe 이번에 Functor f => f을 교체하자를

상관 관계가 명확합니다. 의 바이너리 형식을 테스트하기 위해, Either aFunctor f => f을 교체하자

map :: (b -> c) -> Either a b -> Either a c 

우리는 종종 a -> bba에서 함수의 유형을 표시하지만 정말 Function a b 단지 설탕입니다. 이제 Function 전술 서명에 Either을 긴 양식을 사용하고 교체하자

map :: (b -> c) -> Function a b -> Function a c 

그래서, 함수를 통해 매핑이 우리에게 원래 함수의 반환 값으로 b -> c 함수를 적용하는 기능을 제공합니다.

map :: (b -> c) -> (a -> b) -> (a -> c) 

공지 아무것도 : 우리는 a -> b 설탕을 사용하여 서명을 다시 쓸 수 있을까? compose 유형은 무엇입니까?

compose :: (b -> c) -> (a -> b) -> a -> c 

그래서 compose는 기능 형 단지 map 전문입니다!

두 번째 멋진 점은 a -> bap을 지원할 수 있다는 것입니다. 기능도 상담원 펑터! 이들은 판타지 랜드 사양에 Apply으로 알려져 있습니다.

의는 ap의 유형을 살펴 보자 :

ap :: Apply f => f (b -> c) -> f b -> f c 

가의 대체하자 Apply f => fArray와 : Function a와 지금

ap :: Either a (b -> c) -> Either a b -> Either a c 

, : Either a와 지금

ap :: Array (b -> c) -> Array b -> Array c 

,

ap :: Function a (b -> c) -> Function a b -> Function a c 

Function 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 

Function a 만족에게 적용의 요구 사항을 기억, 그래서 우리는 대체 할 수 Apply f => fFunction 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); 

average([6, 7, 8])의 기능은 무엇입니까? a ([6, 7, 8])은 a -> b 함수 (sum)에 주어지며 b (21)을 생성합니다. aa -> c 함수 (length)에 제공되어 c (3)을 생성합니다. bc을 갖게되었으므로 b -> c -> d 함수 (divide)로 피드하여 d (7)을 생성 할 수 있습니다. 이는 최종 결과입니다.기능 유형이 mapap 지원할 수 있으므로

그래서, 우리는 무료로 converge를 얻을 수 (lift, lift2lift3를 통해). 나는 실제로 그것이 필요하지 않기 때문에 Ramda에서 converge을 제거하고 싶습니다. 내가 의도적으로이 답변에 R.lift를 사용하여 피할


참고. 그것은 어떤 유형의 기능을 지원하기로 결정함에 따라 의미없는 타입 서명과 복잡한 구현이 있습니다. 반면 성역의 아 이티 - 특수 리프팅 기능에는 명확한 유형 서명과 간단한 구현이 있습니다.

+3

:에서

! 이 답변을 블로그 게시물에 올려 놓는 것을보고 싶습니다. –

+0

'(b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)'가 결합 자로 구현 될 때'bird = f = > g => h => x => f (g (x)) (h (x))'. 'psi = f => g => x => y => f (g (x)) (g (y))'와 비슷합니다. 이 새의 이름이 어땠는지 궁금하네요. – ftor

+0

내 자신의 질문에 답하기 위해서 : 그것은'starling '이다.이 함수는'const ap = f => g => x => f (x) (g (x))'에 적용 할 수있는' composition const comp = f => g => x => f (g (x))':'const starling_ = comp (comp (ap))'. – ftor

0

동일한 문제를 이해하는 데 어려움이 있으므로 Ramda의 소스 코드를 살펴보기로했습니다. 앞으로 이것에 관한 blogpost를 쓸 것입니다. 한편 - 나는 Ramda의 lift 작업 방법에 대해 논평을했다. 아주 좋은 https://gist.github.com/philipyoungg/a0ab1efff1a9a4e486802a8fb0145d9e

// 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!