죄송합니다. 저는 제 수학을 잘 모릅니다. 그래서 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습니다.이 경우 일반화 된 버전을 함께 묶는(<*>) 방법이됩니다. 그래서 우리는 그것을 호출하는 것을 상상할 수 있습니까?zipWithfzipWith
이 압축 아이디어는 실제로 우리에게 완전한 원을 제공합니다. 이전에 모노 이드 펑터에 대한 수학 내용을 기억하십니까? 이름에서 알 수 있듯이 다음은 익숙한 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않으며 임의의 유형의 값 을 만들 수 없으므로 mfEmptyas 유형을 수정합니다 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 를 입력하는 함수를 바인딩합니다.
이 세 가지 모두 마음에 규칙적인 기능을 적용하는 것입니다.