나는 “리프팅”이 무엇인지 이해하지 못한다. “리프트”가 무엇인지 이해하기 전에 모나드를 먼저 이해해야합니까? (모나드에 대해서도 완전히 무지합니다.) 아니면 누군가가 간단한 단어로 나에게 설명 할 수 있습니까?
답변
리프팅은 수학적 개념보다 디자인 패턴에 가깝습니다. (여기서는 주변 사람들이 리프트가 카테고리 또는 어떤 방식인지 보여줌으로써 나를 반박 할 것으로 예상하지만).
일반적으로 매개 변수가있는 일부 데이터 유형이 있습니다. 같은 것
data Foo a = Foo { ...stuff here ...}
Foo
숫자 유형 ( Int
, Double
등)을 많이 사용 하고 이러한 숫자를 풀고, 더하거나 곱한 다음 다시 랩핑하는 코드를 계속 작성해야 한다고 가정하십시오 . 랩핑 해제 랩 코드를 한 번 작성하여이를 단락시킬 수 있습니다. 이 기능은 전통적으로 “리프트”라고합니다.
liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
즉, (+)
연산자 와 같은 2 인수 함수 를 사용하여 Foos에 해당하는 함수로 바꾸는 함수가 있습니다.
이제 쓸 수 있습니다
addFoo = liftFoo2 (+)
편집 : 추가 정보
당신은 물론 수 liftFoo3
, liftFoo4
등등. 그러나 이것은 종종 필요하지 않습니다.
관찰로 시작
liftFoo1 :: (a -> b) -> Foo a -> Foo b
그러나 그것은 정확히 동일합니다 fmap
. 그래서 liftFoo1
당신이 쓰는 것보다
instance Functor Foo where
fmap f foo = ...
완전한 규칙 성을 원한다면 말할 수 있습니다.
liftFoo1 = fmap
Foo
functor로 만들 수 있다면 아마도 functor로 만들 수 있습니다. 실제로 작성할 수 있으면 liftFoo2
적용 인스턴스는 다음과 같습니다.
import Control.Applicative
instance Applicative Foo where
pure x = Foo $ ... -- Wrap 'x' inside a Foo.
(<*>) = liftFoo2 ($)
(<*>)
Foo 의 연산자는 다음과 같은 유형입니다.
(<*>) :: Foo (a -> b) -> Foo a -> Foo b
랩핑 된 기능을 랩핑 된 값에 적용합니다. 따라서 구현할 수 있다면 liftFoo2
이것을 관점에서 작성할 수 있습니다. 또는 모듈에 포함되어 liftFoo2
있기 때문에 직접 구현할 수 있습니다.Control.Applicative
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
마찬가지로있다 liftA
및 liftA3
. 그러나 다른 연산자가 있기 때문에 실제로 자주 사용하지는 않습니다.
(<$>) = fmap
이것은 당신이 쓸 수 있습니다 :
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
이 용어 myFunction <$> arg1
는 Foo로 래핑 된 새 함수를 반환합니다. 이것은 차례로 다음을 사용하여 다음 인수에 적용될 수 있습니다 (<*>)
. 이제 모든 arity에 리프트 기능을 사용하는 대신 데이지 체인을 사용합니다.
답변
바울과 야 이르 쿠는 모두 좋은 설명입니다.
들어 올리는 함수는 임의의 수의 인수를 가질 수 있으며 동일한 유형일 필요는 없다고 덧붙이고 싶습니다. 예를 들어 liftFoo1을 정의 할 수도 있습니다.
liftFoo1 :: (a -> b) -> Foo a -> Foo b
일반적으로 1 개의 인수를 취하는 함수의 리프팅은 type 클래스에서 캡처되며 Functor
리프팅 작업은 fmap
다음과 같습니다.
fmap :: Functor f => (a -> b) -> f a -> f b
liftFoo1
의 유형 과의 유사성을 주목하십시오 . 실제로, 가지고 있다면 다음과 같은 인스턴스를 liftFoo1
만들 수 있습니다 .Foo
Functor
instance Functor Foo where
fmap = liftFoo1
또한 임의의 수의 인수로 승화하는 일반화를 적용 스타일 이라고 합니다 . 고정 된 수의 인수로 함수의 리프팅을 파악할 때까지이 작업에 신경 쓰지 마십시오. 그러나 당신이 할 때 , Haskell 이 이것에 대해 좋은 장을 가지고 있음을 배우십시오 . Typeclassopedia는 설명 또 다른 좋은 문서입니다 은 Functor 및 실용적 (뿐만 아니라 다른 종류의 클래스를, 스크롤 해당 문서의 오른쪽 장에 이르기까지).
도움이 되었기를 바랍니다!
답변
예제로 시작해 보겠습니다 (더 명확한 표현을 위해 공백이 추가됨).
> import Control.Applicative
> replicate 3 'a'
"aaa"
> :t replicate
replicate :: Int -> b -> [b]
> :t liftA2
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
> :t liftA2 replicate
liftA2 replicate :: (Applicative f) => f Int -> f b -> f [b]
> (liftA2 replicate) [1,2,3] ['a','b','c']
["a","b","c","aa","bb","cc","aaa","bbb","ccc"]
> ['a','b','c']
"abc"
liftA2
일반 유형의 함수 를 목록 등과 같은에 래핑 된 동일한 유형Applicative
의 함수로 변환합니다 IO
.
또 다른 일반적인 리프트는 lift
입니다 Control.Monad.Trans
. 하나의 모나드의 모나드 동작을 변환 된 모나드의 동작으로 변환합니다.
일반적으로, “리프트” 리프트 (원래의 함수 “는 랩에서”작업에 도달 있도록)는 “포장”유형으로 기능 / 동작을.
이것을 이해하고 모나드 등을 이해하고 유용한 이유를 이해하는 가장 좋은 방법은 아마도 그것을 코딩하고 사용하는 것입니다. 이전에 코딩 한 것이 이로 인해 혜택을받을 수 있다고 생각되는 경우 (즉, 코드를 더 짧게 만드는 등) 시도해 보면 개념을 쉽게 파악할 수 있습니다.
답변
리프팅은 함수를 다른 (일반적으로 더 일반적인) 설정 내에서 해당 함수로 변환 할 수있는 개념입니다.
답변
이 반짝이는 튜토리얼 에 따르면 , functor는 (예를 들어 Maybe<a>
, List<a>
또는 Tree<a>
다른 유형의 요소를 저장할 수 있는) 컨테이너 a
입니다. 나는 <a>
엘리먼트 타입에 자바 제네릭 표기법을 사용 a
했고 엘리먼트를 트리의 베리로 생각했다 Tree<a>
. 함수가 fmap
소자 변환 기능, 소요 a->b
및 용기 functor<a>
. 그것은 적용 a->b
효율적으로 변환 컨테이너의 모든 요소 functor<b>
. 첫 번째 인수 만 제공되면 a->b
을 fmap
기다립니다 functor<a>
. 즉, 공급 a->b
만하면이 요소 레벨 기능 functor<a> -> functor<b>
이 컨테이너 에서 작동 하는 기능으로 바뀝니다 . 이것을 리프팅 이라고합니다기능의. 컨테이너는 functor 라고도 하므로 Monads 대신 Functors가 리프팅의 전제 조건입니다. 모나드는 리프팅에 대한 “병렬”의 일종입니다. 둘 다 Functor 개념에 의존하고 있습니다 f<a> -> f<b>
. 차이점은 a->b
전환에 리프팅 사용 을 사용 하는 반면 Monad는 사용자가 정의해야한다는 것 a -> f<b>
입니다.