죄송합니다. 저는 제 수학을 잘 모릅니다. 그래서 Applicative typeclass에서 함수를 어떻게 발음하는지 궁금합니다.
당신의 수학을 안다는 것은 여기에서 대체로 무관하다고 생각합니다. 아시다시피 Haskell은 추상 수학의 다양한 분야, 특히 범주 이론 에서 몇 가지 용어를 차용합니다 . 여기서 펑터와 모나드를 얻습니다. Haskell에서 이러한 용어의 사용은 공식적인 수학적 정의와 다소 차이가 있지만, 어쨌든 좋은 설명 용어가 될만큼 일반적으로 충분히 가깝습니다.
Applicative
형 클래스 사이의 어딘가에 앉아 Functor
와 Monad
하나가 유사한 수학적 근거가 기대 있도록. Control.Applicative
모듈 설명서는 다음으로 시작됩니다.
이 모듈은 펑터와 모나드 사이의 중간 구조를 설명합니다. 순수한 표현과 시퀀싱을 제공하지만 바인딩은 제공하지 않습니다. (기술적으로는 강력한 느슨한 모노 이드 펑터입니다.)
흠.
class (Functor f) => StrongLaxMonoidalFunctor f where
. . .
아니 꽤 인기를 얻기 쉬운으로 Monad
, 나는 생각한다.
이 모든 것이 기본적으로 요약되는 Applicative
것은 수학적으로 특히 흥미로운 개념과 일치하지 않기 때문에 Haskell에서 사용되는 방식을 포착하는 기성 용어가 주변에 존재하지 않는다는 것입니다. 따라서 지금은 수학을 제쳐두십시오.
우리가 무엇을 부를지 알고 싶다면 (<*>)
그것이 기본적으로 무엇을 의미하는지 아는 것이 도움이 될 것입니다.
그래서 함께이야 Applicative
어쨌든, 왜 합니까 우리는 그 전화를?
무엇 Applicative
실제로 금액은 들어 올릴 수있는 방법은 임의의 에 기능을 Functor
. Maybe
(가장 단순 Functor
하고 사소하지 않은 ) 및 Bool
(가장 단순한 사소하지 않은 데이터 유형) 의 조합을 고려하십시오 .
maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not
이 기능은 fmap
우리가 들어 있습니다 not
작업에서 Bool
작업에 Maybe Bool
. 하지만 우리가 들어 올리고 싶다면 (&&)
?
maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)
글쎄, 그건 우리가 원하는 게 아니에요 전혀 ! 사실, 그것은 거의 쓸모가 없습니다. 우리는 영리 해 지려고 노력할 수 있고 다른 사람 Bool
을 등을 Maybe
통해 몰래 들어갈 수 있습니다 ...
maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)
...하지만 그것은 좋지 않습니다. 한 가지는 잘못된 것입니다. 또 다른 것은 추악 합니다. 우리는 계속해서 시도 할 수 있지만 , 임의의Functor
. 성가신!
반면에 Maybe
의 Monad
인스턴스를 사용하면 쉽게 할 수 있습니다 .
maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
y' <- y
return (x' && y')
이제는 단순한 함수를 번역하는 데 많은 번거 로움이 있습니다. 그래서 Control.Monad
자동으로 수행하는 함수를 제공합니다 liftM2
. 이름에서 2는 정확히 두 개의 인수의 함수에서 작동한다는 사실을 나타냅니다. 3, 4 및 5 인수 함수에 대해 유사한 함수가 존재합니다. 이러한 함수는 더 좋지만 완벽하지는 않으며 인수 수를 지정하는 것은 추하고 서투른 작업입니다.
어느 우리에게 가져다 실용적 타입의 클래스를 도입 종이 . 여기에서 저자는 본질적으로 두 가지 관찰을합니다.
- 다중 인수 함수를 a
Functor
로 해제하는 것은 매우 자연스러운 일입니다.
- 그렇게하기 위해 모든 기능이 필요하지는 않습니다.
Monad
일반 함수 응용 프로그램은 용어의 단순한 병치에 의해 작성되므로 "리프트 된 응용 프로그램"을 가능한 한 간단하고 자연스럽게 만들기 위해이Functor
문서에서는 응용 프로그램을위한 중위 연산자를 소개 하고으로 들어 올려 그에 필요한 것을 제공하는 유형 클래스를 소개합니다. .
이 모든 것이 우리에게 다음과 같은 요점을 가져다줍니다. (<*>)
단순히 함수 응용을 나타냅니다. 그렇다면 공백 "병치 연산자"를 사용하는 것과 다르게 발음하는 이유는 무엇입니까?
하지만 그다지 만족스럽지 않다면 Control.Monad
모듈이 모나드에 대해 동일한 작업을 수행하는 함수도 제공 한다는 것을 알 수 있습니다 .
ap :: (Monad m) => m (a -> b) -> m a -> m b
어디 ap
"적용"에 대한 짧은 물론이다. 어떤 때문에 Monad
할 수 있습니다 Applicative
, 그리고 ap
기능은 후자에 제시의 하위 집합 만 필요, 우리는 아마도 말할 수있는 경우 (<*>)
운영자하지 않았다, 그것은 호출해야합니다 ap
.
다른 방향에서도 접근 할 수 있습니다. Functor
리프팅 작업이라고 fmap
는 일반화이기 때문에 map
목록에 작업이. 목록에서 어떤 종류의 기능이 작동 (<*>)
합니까? 이 무슨 ap
물론, 목록에 않지만, 그 자체에 특히 유용 아니다.
실제로 목록에 대한보다 자연스러운 해석이있을 수 있습니다. 다음 유형 서명을 보면 무엇이 떠오르나요?
listApply :: [a -> b] -> [a] -> [b]
목록을 병렬로 정렬하여 첫 번째의 각 기능을 두 번째의 해당 요소에 적용하는 아이디어에 대해 너무 유혹적인 것이 있습니다. 우리의 오랜 친구에게는 안타깝게도이Monad
간단한 작업 은 목록의 길이가 다른 경우 모나드 법칙을 위반합니다 . 그러나 그것은 괜찮 Applicative
습니다.이 경우 일반화 된 버전을 함께 묶는(<*>)
방법이됩니다. 그래서 우리는 그것을 호출하는 것을 상상할 수 있습니까?zipWith
fzipWith
이 압축 아이디어는 실제로 우리에게 완전한 원을 제공합니다. 이전에 모노 이드 펑터에 대한 수학 내용을 기억하십니까? 이름에서 알 수 있듯이 다음은 익숙한 Haskell 유형 클래스 인 모노 이드와 펑터의 구조를 결합하는 방법입니다.
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Monoid a where
mempty :: a
mappend :: a -> a -> a
상자에 넣어서 약간 흔들면 어떻게 생겼을까 요? 에서 Functor
우리는의 생각하겠습니다 그 형식 매개 변수의 구조 무관 하고,에서 Monoid
우리가 함수의 전체 형태를 유지하는 것입니다 :
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ?
mfAppend :: f ? -> f ? -> f ?
우리는 진정으로 "빈"을 생성하는 방법이 있다고 가정하고 싶지 Functor
않으며 임의의 유형의 값 을 만들 수 없으므로 mfEmpty
as 유형을 수정합니다 f ()
.
또한 mfAppend
일관된 유형 매개 변수가 필요 하도록 강요하고 싶지 않으므로 이제 다음과 같이합니다.
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f ?
결과 유형은 mfAppend
무엇입니까? 우리는 우리가 전혀 모르는 두 가지 임의의 유형이 있으므로 많은 옵션이 없습니다. 가장 현명한 것은 다음 두 가지를 모두 유지하는 것입니다.
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f (a, b)
어느 시점 에서 목록 mfAppend
의 일반화 된 버전이 명확하게 zip
표시되며 Applicative
쉽게 재구성 할 수 있습니다.
mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)
이것은 또한 pure
의 식별 요소와 관련되어 있음을 보여 Monoid
주므로 다른 좋은 이름은 단위 값, null 연산 등을 제안하는 모든 것일 수 있습니다.
길이가 길어서 요약하면 다음과 같습니다.
(<*>)
단지 수정 된 함수 응용 프로그램이므로 "ap"또는 "apply"로 읽거나 일반 함수 응용 프로그램처럼 완전히 제거 할 수 있습니다.
(<*>)
또한 대략적 zipWith
으로 목록을 일반화 하므로 fmap
"map a functor with" 로 읽는 것과 유사하게 "zip functors with"로 읽을 수 있습니다 .
첫 번째는 Applicative
이름에서 알 수 있듯이 유형 클래스 의 의도에 더 가깝기 때문에 이것이 제가 권장하는 것입니다.
사실, 저는 해제 된 모든 응용 프로그램 운영자를 자유롭게 사용하고 비 발음을 권장합니다 .
(<$>)
, 단일 인수 함수를 Functor
(<*>)
, 다중 인수 함수를 Applicative
(=<<)
, Monad
기존 계산에 a 를 입력하는 함수를 바인딩합니다.
이 세 가지 모두 마음에 규칙적인 기능을 적용하는 것입니다.