code

'Alternative'유형 클래스의 의미와 다른 유형 클래스와의 관계로 인해 혼동 됨

codestyles 2020. 12. 8. 08:07
반응형

'Alternative'유형 클래스의 의미와 다른 유형 클래스와의 관계로 인해 혼동 됨


내가 겪고 있었어요 Typeclassopedia 유형 클래스를 배울 수 있습니다. 나는 이해하고 있습니다 Alternative(그리고 MonadPlus그 문제에 대해).

내가 겪고있는 문제 :

  • 'pedia는 "Alternative 유형 클래스는 monoid 구조를 가진 Applicative functor를위한 것입니다."라고 말합니다. 이해가 안가요-Alternative가 Monoid와 완전히 다른 것을 의미하지 않나요? 즉, Alternative 유형 클래스의 요점을 두 가지 중에서 선택하는 것으로 이해 한 반면 Monoids는 사물을 결합하는 것으로 이해했습니다.

  • Alternative에 empty메소드 / 멤버 가 필요한 이유는 무엇입니까? 내가 틀렸을 수도 있지만, 적어도 내가 찾을 수 있는 코드 에서는 전혀 사용되지 않는 것 같습니다 . 수업 주제와 맞지 않는 것 같습니다. 두 가지가 있고 하나를 선택해야하는 경우 '비어 있음'이 필요한 것은 무엇입니까?

  • Alternative 유형 클래스에 Applicative 제약이 필요한 이유는 무엇이며, 왜 그런가요 * -> *? 왜 안돼 <|> :: a -> a -> a? 모든 인스턴스는 여전히 같은 방식으로 구현 될 수 있습니다 ... 제 생각에는 (확실하지 않습니다). Monoid가 제공하지 않는 가치는 무엇입니까?

  • MonadPlus유형 클래스 의 요점은 무엇 입니까? a Monad둘 다로 사용하여 모든 장점을 잠금 해제 할 수 없습니까 Alternative? 그냥 버리지 않는 이유는 무엇입니까? (내가 틀렸다고 확신하지만 반례가 없습니다)

이 모든 질문이 일관성이 있기를 바랍니다.


현상금 업데이트 : @Antal의 대답은 좋은 시작이지만 Q3는 아직 열려 있습니다. Alternative는 Monoid가 제공하지 않는 것은 무엇입니까? 나는 발견 이 대답 은 구체적인 예제 부족하고, 대안의 높은 kindedness는 모노 이드 구별 방법에 대한 구체적인 토론 이후 불만족을.

응용 프로그램의 효과를 Monoid의 동작과 결합하려는 경우 다음과 같이하면 안됩니다.

liftA2 mappend

많은 Monoid 인스턴스가 Alternative 인스턴스와 정확히 동일하기 때문에 이것은 나에게 훨씬 더 혼란 스럽습니다.

그렇기 때문에 Alternative가 왜 필요한지, 그리고 그것이 Monoid와 어떻게 다른지 또는 다른 것을 의미하는지 보여주는 구체적인 예찾고 있습니다.


우선, 이러한 각 질문에 대한 짧은 답변을 제공하겠습니다. 그런 다음 각각을 더 자세한 답변으로 확장 할 것이지만,이 짧은 답변이 탐색에 도움이되기를 바랍니다.

  1. 아니, AlternativeMonoid다른 것을 의미하지 않는다; 및의 Alternative구조를 모두 갖는 유형을위한 것 Applicative입니다 Monoid. "피킹"과 "결합"은 동일한 더 넓은 개념에 대한 두 가지 다른 직관입니다.

  2. Alternative포함 empty뿐만 아니라 <|>디자이너가이 유용하다고 생각했기 때문에,이는 모노 이드를 초래하기 때문이다. 피킹 측면에서는 empty불가능한 선택에 해당합니다.

  3. 우리는 모두를 필요로 Alternative하고 Monoid있기 때문에 이전의 순종 (또는해야) 후자보다 더 법률; 이러한 법칙은 유형 생성자의 단일 및 적용 구조와 관련됩니다. 또한 Alternative내부 유형에 의존 할 수 없지만 Monoid할 수 있습니다.

  4. MonadPlusAlternative더 많은 법률을 준수해야하므로 보다 약간 강합니다 . 이 법칙은 적용 구조에 추가하여 모노 이드 구조를 모노 구조와 관련시킵니다. 둘 다의 인스턴스가 있으면 일치해야합니다.


하지 않는 Alternative완전히 다른 의미 뭔가를 Monoid?

별로! 혼란 Monoid스러운 이유 중 일부 는 Haskell 클래스가 꽤 나쁜 (잘, 불충분하게 일반적인) 이름을 사용하기 때문입니다. 다음은 수학자가 모노 이드를 정의하는 방법입니다 (매우 명시적임).

정의. 모노 이드는 세트 인 M 뛰어난 소자를 구비 εM : 이진 연산자 · M × MM , 병치 붙이고, 다음 두 조건이 유지되도록 :

  1. ε 는 동일성입니다 : 모든 mM , m ε = ε m = m .
  2. · 연관성 : 모든 m ₁, m ₂, m ₃ ∈ M , ( mm ₂) m ₃ = m ₁ ( mm ₃).

그게 다야. 하스켈에서, ε의 철자가 mempty와 · 철자 mappend(또는, 요즘 <>), 그리고 세트 M은 유형입니다 M에서 instance Monoid M where ....

이 정의를 살펴보면 "결합"(또는 해당 문제에 대한 "선택")에 대해 아무 말도하지 않습니다. ·와 ε대해 말하지만 그게 다입니다. 이제, 사물을 결합 하는 것이이 구조와 잘 어울린다는 것은 확실히 사실입니다. ε 은 사물이없는 것에 해당하고, mm ₂는 m ₁과 m ₂의 물건을 함께 뭉치면 모든 것을 포함하는 새로운 것을 얻을 수 있다고 말합니다 . 그러나 여기에 대안적인 직관이 있습니다. ε 은 선택의 여지가 전혀 없음에 해당하고 mm ₂는 m ₁과 m 사이의 선택에 해당합니다₂. 이것이“선택”직관입니다. 둘 다 모노 이드 법칙을 준수합니다.

  1. 아무것도없는 것과 선택의 여지가없는 것이 모두 정체성입니다.
    • 내가 아무것도 가지고 있지 않고 몇 가지 물건과 함께 먹으면 다시 같은 물건으로 끝납니다.
    • 선택의 여지가 전혀없는 (불가능한 일)과 다른 선택 중 하나를 선택할 수 있다면 다른 (가능한) 선택을 선택해야합니다.
  2. 컬렉션을 함께 살펴보고 선택하는 것은 모두 연관성이 있습니다.
    • 내가 세 가지 컬렉션을 가지고 있다면 처음 두 개를 함께 모은 다음 세 번째 것 또는 마지막 두 개를 함께 모은 다음 첫 번째 것을 모아도 상관 없습니다. 어느 쪽이든, 나는 동일한 총 glommed 컬렉션으로 끝납니다.
    • 세 가지 중 하나를 선택할 수 있다면 (a) 먼저 첫 번째 또는 두 번째와 세 번째 중에서 선택한 다음 필요한 경우 첫 번째와 두 번째 중에서 선택하거나 (b) 먼저 첫 번째 중에서 선택하는지 여부는 중요하지 않습니다. 2 ~ 3 위, 그리고 필요하다면 2 ~ 3 위 사이에요. 어느 쪽이든 원하는 것을 선택할 수 있습니다.

(참고 : 저는 여기에서 빠르고 느슨하게 플레이하고 있습니다. 그것이 바로 직감적 인 이유입니다. 예를 들어, 다음을 기억하는 것이 중요합니다. · 교환적일 필요는 없습니다. 위 내용은 다음과 같습니다. mm ₂ ≠ mm ₁ .)

보라 : 이러한 종류의 것들 (그리고 다른 많은 것들-곱하는 숫자가 실제로 "결합" 또는 "선택"입니까?)은 동일한 규칙을 따릅니다. 직관을 갖는 것은 이해를 발전시키는 데 중요하지만 실제로 무슨 일 벌어지고 있는지를 결정하는 것은 규칙과 정의입니다 .

그리고 가장 좋은 점은이 두 가지 직관이 동일한 캐리어에 의해 해석 될 수 있다는 것입니다! 하자 M은 (하지의 세트 세트의 일부 세트로 모든 하자, 빈 세트를 포함! 세트) ε 빈 세트를 ∅, 그리고 ·이 ∪ 세트 조합이 될 수 있습니다. ∅이 ∪의 동일성이고 ∪이 연관성임을 쉽게 알 수 있으므로 ( M , ∅, ∪)이 모노 이드 라는 결론을 내릴 수 있습니다 . 지금:

  1. 세트를 사물의 모음이라고 생각하면 ∪는 더 많은 것을 얻기 위해 함께 빛나게하는 것, 즉“결합”직관에 해당합니다.
  2. 집합을 가능한 행동을 나타내는 것으로 생각한다면, ∪는 선택할 수있는 가능한 행동 풀을 늘리는 것에 해당합니다.

그리고 이것이 바로 []Haskell 에서 진행되고있는 일 입니다 : [a]is a Monoidfor all a이며, []비결정론을 표현하기 위해 응용 펑터 (및 모나드)가 사용됩니다. 결합 및 피킹 직관 모두 동일한 유형 : mempty = empty = []mappend = (<|>) = (++).

따라서 Alternative클래스는 (a) 적용 가능한 펑터이고 (b) 유형에서 인스턴스화 될 때 몇 가지 규칙을 따르는 값과 이진 함수를 갖는 객체를 나타 내기 위해 존재합니다. 어떤 규칙? 모노 이드 규칙. 왜? 유용하다고 판명 되었기 때문에 :-)


Alternative비어있는 메서드 / 멤버 필요한 이유는 무엇 입니까?

음, 은밀한 대답은 " Alternative모노 이드 구조를 나타 내기 때문에 "입니다. 그러나 진짜 질문은 : 모노 이드 구조인가? 왜 반 그룹, ε 없는 모노 이드가 아닌가? 한 가지 대답은 모노 이드가 더 유용하다고 주장하는 것입니다. 나는 많은 사람들 (아마도 Edward Kmett아님 )이 이에 동의 할 것이라고 생각합니다. 거의 항상, 당신이 현명한 (<|>)/ mappend/ · 을 가지고 있다면 , 당신은 현명한 empty/ mempty/ ε 을 정의 할 수있을 것 입니다. 반면에 우산 아래에 더 많은 물건을 놓을 수 있기 때문에 추가 일반성을 갖는 것이 좋습니다.

또한 이것이 "픽킹"직관과 어떻게 맞 물리는지 알고 싶습니다. 어떤 의미에서 정답은“ '따르는'직관을 버릴 때를 아는 것”이라는 것을 염두에두고 두 가지를 통합 있다고 생각합니다 . []비결정론에 적용 가능한 펑터를 고려하십시오 . 나는 두 가지 유형의 값 결합 할 경우 [a](<|>)nondeterministically 왼쪽에서 작업 또는 오른쪽에서 작업 중 하나를 따기에 있음을 해당합니다. 그러나 때로는 한쪽에서 가능한 조치가 없을 수도 있습니다. 괜찮습니다. 마찬가지로 파서를 고려하면(<|>)왼쪽에있는 내용 또는 오른쪽에있는 내용 ( "선택")을 구문 분석하는 파서를 나타냅니다. 그리고 항상 실패하는 파서를 가지고 있다면 그것은 결국 신원이됩니다. 만약 당신이 그것을 선택한다면, 당신은 즉시 그 선택을 거부하고 다른 하나를 시도합니다.

이 모든 것이 기억 말했다 것입니다 거의 같은 클래스가 전적으로 가능 Alternative하지만, 부족 empty. 그것은 완벽하게 타당 할 것입니다 – 그것은 심지어 슈퍼 클래스 Alternative수도 있습니다 – 그러나 Haskell이 한 일이 아닙니다. 아마도 이것은 유용한 것에 대한 추측에서 벗어난 것입니다.


Alternative유형 클래스에 Applicative제약 조건이 필요한 이유는 무엇 이며 왜 일종의 제약 조건이 필요 * -> *합니까? … 왜 그냥 [사용]하지 liftA2 mappend않습니까?

음,이 세 가지 제안 된 변경의 각 생각해 보자 다음 치우는 Applicative제한 조건을 Alternative; Alternative의 인수 의 종류 변경 ; 및 사용 liftA2 mappend대신 <|>하고 pure mempty대신 empty. 이 세 번째 변경 사항은 가장 다르기 때문에 먼저 살펴 보겠습니다. 우리가 Alternative완전히 제거 하고 클래스를 두 개의 일반 함수로 대체 했다고 가정 합니다.

fempty :: (Applicative f, Monoid a) => f a
fempty = pure mempty

(>|<) :: (Applicative f, Monoid a) => f a -> f a -> f a
(>|<) = liftA2 mappend

some의 정의를 유지할 수도 many있습니다. 그리고 이것은 않는다 는 사실, 우리에게 모노 이드 구조를 제공합니다. 그러나 그것은 우리에게 잘못된 것을주는 것 같습니다. 의 인스턴스가 아니므로 Just fst >|< Just snd실패 해야합니다 . 아니,하지만의는 무엇을 위의 코드에서 발생할 것이라는 점을 우리는 모노 이드 인스턴스입니다. 원하는가 (빌려 용어에 대한 내부 형 무관의 하나 인 마태 복음 Farkas 보낸 사람 - 반다이 크 에서 매우 관련 하스켈 - 카페 토론 매우 유사한 질문을); 모노 이드가 결정 구조에 관하여는 의 구조 아닌 구조 의 인수.(a,a) -> aMonoidAlternativeff

이제 Alternative일종의 유형 클래스 로 떠나고 싶다고 생각 했으므로 이를 변경하기 위해 제안 된 두 가지 방법을 살펴 보겠습니다. 우리는 종류를 변경하는 경우, 우리는 의 없애 Applicative제약; Applicative종류에 대해서만 이야기 * -> *하므로 참조 할 방법이 없습니다. 두 가지 가능한 변경이 남습니다. 첫 번째, 더 사소한 변경은 Applicative제약 조건을 제거 하고 종류는 그대로 두는 것입니다.

class Alternative' f where
  empty' :: f a
  (<||>) :: f a -> f a -> f a

다른 큰 변화는 Applicative제약 조건을 없애고 종류를 변경하는 것입니다.

class Alternative'' a where
  empty'' :: a
  (<|||>) :: a -> a -> a

두 경우 모두 some/를 제거해야 many하지만 괜찮습니다. (Applicative f, Alternative' f) => f a -> f [a]또는 유형을 사용하여 독립형 함수로 정의 할 수 있습니다 (Applicative f, Alternative'' (f [a])) => f a -> f [a].

이제 유형 변수의 종류를 변경하는 두 번째 경우에서 클래스가 정확히 똑같다는 것을 알 Monoid수 있습니다 (또는 제거하려는 경우 empty'', Semigroup). 따라서 별도의 클래스를 갖는 것이 이점이 없습니다. 그리고 사실, 우리는 혼자 종류의 변수를두고 있지만 제거 할 경우에도 Applicative, 제약 Alternative단지가됩니다 forall a. Monoid (f a)우리는 심지어 모든 멋진 GHC 확장과 함께, 하스켈 이러한 정량화 제약을 쓸 수 있지만,. (이것은 위에서 언급 한 내부 유형 불가지론을 표현한다는 점에 유의하십시오.) 따라서 이러한 변경 중 하나를 수행 할 수 있다면 유지할 이유가 없습니다 Alternative(정량화 된 제약을 표현할 수 있다는 점을 제외하고는 거의 설득력이 없어 보입니다). .

따라서 질문 은 "두 부분의 인스턴스 인 Alternative부분과 부분 간에 관계가 있습니까?"로 요약됩니다. 그리고 문서에는 아무것도 없지만 나는 입장을 취하고 라고 말할 것입니다. 또는 적어도 그럴 필요 있습니다. 나는 그것이 (모노 이드 법칙에 추가하여) 관련 법률을 준수해야 한다고 생각합니다 . 특히 그 법칙은ApplicativefAlternativeApplicative

  1. 오른쪽 분포 (의 <*>) :  (f <|> g) <*> a = (f <*> a) <|> (g <*> a)
  2. 오른쪽 흡수 (용 <*>) :  empty <*> a = empty
  3. 왼쪽 분포 (/ fmap) :  f <$> (a <|> b) = (f <$> a) <|> (f <$> b)
  4. 왼쪽 흡수 (용 fmap) :  f <$> empty = empty

이러한 법률에 대한 사실을 표시 []하고 Maybe, 그리고 (그 척 MonadPlus인스턴스가 인 Alternative경우) IO,하지만 난 어떤 증거 나 철저한 테스트를 수행하지 않았습니다. (예를 들어, 원래는 왼쪽 분배가를 유지 한다고 생각 <*>했지만 이것은를위한 잘못된 순서로 "효과를 수행"합니다 [].) 그러나 비 MonadPlus유적으로 유사한 법칙을 준수 할 것으로 예상되는 것은 사실입니다 ( 분명히 일부 모호함 ). 나는 원래 자연스러워 보이는 제 3 법칙을 주장하고 싶었습니다.

  • 왼쪽 흡수 (용 <*>) :  a <*> empty = empty

그러나, 나는 믿고 있지만 []Maybe이 법에 순종 IO하지 않는, 나는 그것을 필요로하지 않는 것이 좋습니다 (단락의 다음 몇 명백해질 것이다 이유)라고 생각한다.

그리고 실제로 Edward Kmett 비슷한 견해를지지하는 슬라이드를 몇가지고있는 것 같습니다 . 그것에 들어가기 위해 우리는 좀 더 수학적 전문 용어를 포함하는 간단한 여담을 가져야 할 것입니다. 마지막 슬라이드 인 "I Want More Structure"는 "올바른 세미 니어링이 대안에 대한 것이므로 Monoid는 Applicative에 있습니다."그리고 "Applicative의 주장을 버리면 Monoid를 얻습니다. 대안의 주장을 버리면 RightSemiNearRing을 얻게됩니다.”

세미 니어링? "어떻게 올바른 세미 니어링이 들어갔습니까?" 나는 당신이 우는 것을 들었습니다. 잘,

정의. 바로 근처 semiring (도 바로 seminearring 하지만, 이전보다 구글에 사용되는 것)을 배입니다 ( R은 , + · 0) 여기서 ( R , +, 0) 인 모노 이드 ( R , ·) 세미 그룹이며 다음 두 조건이 유지됩니다.

  1. · +에 대한 우 분배 : 모든 r , s , tR , ( s + t ) r = sr + tr .
  2. 0은 ·에 대해 오른쪽 흡수입니다. 모든 rR , 0 r = 0입니다.

A는 거의 semiring 왼쪽 유사하게 정의된다.

이제 이것은 제대로 작동하지 않습니다. 왜냐하면 <*>진정한 연관성 또는 이항 연산자가 아니기 때문 입니다. 유형이 일치하지 않습니다. 나는 이것이 Edward Kmett가“논쟁을 버리고”에 대해 말할 때 얻는 것이라고 생각합니다. 또 다른 옵션은 (이 맞다면 내가 확실 해요) 우리가 실제로 원하는 (말할 수 있습니다 f a, <|>, <*>, empty)를 형성 거의 semiringoid 권리 은 "-oid"접미사는 이항 연산자 만 적용 할 수 있음을 나타냅니다 어디에, 특정 요소 쌍 ( 일라 그룹 형태 ). 그리고 우리는 그 말을하고 싶은 것 ( f a, <|>, <$>, empty이 이대로의 조합에 따라 수 있지만)이 근처에-semiringoid 방치Applicative법칙과 오른쪽에 가까운 세미 링고 이드 구조. 그러나 이제 나는 머리 위로 들어가고 있으며 이것은 어쨌든별로 관련이 없습니다.

어쨌든 이러한 법칙 은 모노 이드 법칙보다 강하기 때문에 완벽하게 유효한 Monoid인스턴스가 유효하지 않은 Alternative인스턴스 가 될 것 입니다. 표준 라이브러리에는 (적어도) 두 가지 예가 있습니다 : Monoid a => (a,)Maybe. 각각을 빠르게 살펴 보겠습니다.

두 개의 monoid가 주어지면 그 제품은 monoid입니다. 결과적으로 튜플은 Monoid명백한 방법으로 인스턴스를 만들 수 있습니다 ( 기본 패키지의 소스를 다시 포맷 ) :

instance (Monoid a, Monoid b) => Monoid (a,b) where
  mempty = (mempty, mempty)
  (a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)

마찬가지로, 첫 번째 구성 요소가 monoid 요소 인 튜플을 Applicativemonoid 요소를 누적하여 인스턴스로 만들 수 있습니다 ( 기본 패키지의 소스 형식을 다시 지정 ).

instance Monoid a => Applicative ((,) a) where
  pure x = (mempty, x)
  (u, f) <*> (v, x) = (u `mappend` v, f x)

그러나 튜플의 인스턴스가 아닌 Alternative그들 monoidal 구조 위에-가 될 수 없기 때문에, Monoid a => (a,b)모든 종류 존재하지 bAlternative의 monoidal 구조는 내부 형 무관해야한다. b를 표현할 수 있으려면 모나드가 되어야 뿐만 아니라 함수에 (f <> g) <*> a대한 Monoid인스턴스 를 사용해야합니다 Monoid b => a -> b. 심지어 우리가 필요한 모든 monoidal 구조를 가지고있는 경우에, 그것을 위반하는 네 가지Alternative법을. 이것을 보려면 let ssf n = (Sum n, (<> Sum n))과 let ssn = (Sum n, Sum n). 그런 다음, 쓰기 (<>)위해 mappend, 우리는 (가끔 유형 약어로, GHCi에서 확인할 수 있습니다) 다음과 같은 결과를 얻을 :

  1. 올바른 분포 :
    • (ssf 1 <> ssf 1) <*> ssn 1 = (Sum 3, Sum 4)
    • (ssf 1 <*> ssn 1) <> (ssf 1 <*> ssn 1) = (Sum 4, Sum 4)
  2. 올바른 흡수 :
    • mempty <*> ssn 1 = (Sum 1, Sum 0)
    • mempty = (Sum 0, Sum 0)
  3. 왼쪽 분포 :
    • (<> Sum 1) <$> (ssn 1 <> ssn 1) = (Sum 2, Sum 3)
    • ((<> Sum 1) <$> ssn 1) <> ((<> Sum 1) <$> ssn 1) = (Sum 2, Sum 4)
  4. 왼쪽 흡수 :
    • (<> Sum 1) <$> mempty = (Sum 0, Sum 1)
    • mempty = (Sum 1, Sum 1)

다음으로 Maybe. 현재로서는 MaybeMonoidAlternative사례가 동의하지 않습니다 . (하지만 하스켈 - 카페 토론 I이 섹션의 시작 부분에 언급이 변경 제안하고, 거기에 반군 패키지에서 newtype은 같은 효과를 얻을 것입니다.) A와 , 사용하여 monoids에 반군 리프트 신분으로; 기본 패키지에는 semigroup 클래스가 없기 때문에 monoid 만 들어 올려서 ( 기본 패키지의 소스를 다시 형식화) 다음 을 얻습니다 .OptionMonoidMaybeNothing

instance Monoid a => Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m       = m
  m       `mappend` Nothing = m
  Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)

한편,로 Alternative, Maybe실패와 우선 순위의 선택이고, 우리는 (다시 포맷 얻을 수 있도록 기본 패키지의 소스 ) :

instance Alternative Maybe where
  empty = Nothing
  Nothing <|> r = r
  l       <|> _ = l

그리고 후자만이 Alternative법을 충족한다는 것이 밝혀졌습니다 . Monoid예를 덜 심하게보다는 실패 (,)의; 그것은 수행 에 대한 법을 준수 <*>거의 의해 사고가의 유일한 인스턴스의 행동 형성되어 있지만, Monoid(위에서 언급 한 바와 같이) 기능을 리프트하는 기능에 대한을 그 독자 실용적 펑터에 반환 monoids은. 당신이 그것을 해결하는 경우, 당신이 그 권리를 분배 법칙과 오른쪽 흡수를 찾을 수 있습니다 (이것은 모두 아주 기계의) <*>모두에 대한 모든 보류 AlternativeMonoid대한 인스턴스로하지 왼쪽 흡수 fmap. 그리고위한 왼쪽 분배 법칙은 fmap에 대한 파악을하지 Alternative다음과 같이 예 :

f <$> (Nothing <|> b)
  = f <$> b                          by the definition of (<|>)
  = Nothing <|> (f <$> b)            by the definition of (<|>)
  = (f <$> Nothing) <|> (f <$> b)    by the definition of (<$>)

f <$> (Just a <|> b)
  = f <$> Just a                     by the definition of (<|>)
  = Just (f a)                       by the definition of (<$>)
  = Just (f a) <|> (f <$> b)         by the definition of (<|>)
  = (f <$> Just a) <|> (f <$> b)     by the definition of (<$>)

그러나 Monoid인스턴스에 대해서는 실패 합니다. 기록 (<>)을 위해 mappend, 우리는이 :

  • (<> Sum 1) <$> (Just (Sum 0) <> Just (Sum 0)) = Just (Sum 1)
  • ((<> Sum 1) <$> Just (Sum 0)) <> ((<> Sum 1) <$> Just (Sum 0)) = Just (Sum 2)

이제이 예제에 대한 한 가지주의 사항이 있습니다. 만 요구할 경우 Alternative들과의 호환성 수 <*>, 그리고 함께 <$>, 다음 Maybe괜찮습니다. 위에서 언급 한 Edward Kmett의 슬라이드는를 참조하지 않지만 이에 <$>관한 법률도 요구하는 것이 합리적이라고 생각합니다. 그럼에도 불구하고 나는 이것에 대해 나를 뒷받침 할 어떤 것도 찾을 수 없다.

따라서 우리는가되는 Alternative것이 a 가되는 것보다 더 강력한 요구 사항 Monoid이므로 다른 클래스가 필요 하다는 결론을 내릴 수 있습니다 . 이것의 가장 순수한 예는 내부 유형 불가지론 적 Monoid인스턴스를 가진 유형 Applicative과 서로 호환되지 않는 인스턴스입니다. 그러나 기본 패키지에는 이러한 유형이 없으며 생각할 수 없습니다. (아무것도 존재하지 않을 수도 있지만 놀랄 것입니다.) 그럼에도 불구하고 이러한 내부 유형의 영지주의 예제는 두 유형 클래스가 달라야하는 이유를 보여줍니다.


MonadPlus유형 클래스 의 요점은 무엇입니까 ?

MonadPlus,처럼 Alternative,의 강화 Monoid이지만 Monad대신 Applicative. 에드워드 Kmett에 따르면 그의 대답 질문에 "typeclasses 사이의 구별 MonadPlus, Alternative그리고 Monoid?" , MonadPlus이다 또한 보다 강력한 Alternative법 : empty <*> a예를 들어, 그 의미하지는 않습니다 empty >>= f. AndrewC이에 대한 두 가지 예를 제공합니다 Maybe. 문제는이 있다는 사실에 의해 복잡 하기위한 법률이 개 잠재적 인 세트MonadPlus . 보편적 것을 동의 MonadPlus와 모노 이드를 형성하도록되어 mplus하고 mempty, 그리고는 만족 해야하는 왼쪽 제로법률, mempty >>= f = mempty. Hhowever 일부 MonadPlusSES 만족 왼쪽의 분포 , mplus a b >>= f = mplus (a >>= f) (b >>= f); 다른 사람이 만족하는 캐치를 왼쪽 , mplus (return a) b = return a. (에 대해 0 / 배포를 왼쪽 참고 MonadPlus바로 분배 법칙 유사하다 / 대한 흡수 Alternative, (<*>)더 유사 (=<<)보다는 (>>=)왼쪽 분포는 어떤 있도록 "더 나은"아마도.) MonadPlus만족이 같은 캐치를 왼쪽 예 Maybe,입니다 Alternative하지만 제 1 종 / MonadPlus. 왼쪽 캐치 순서에 의존하기 때문에, 당신은에 대한 newtype은 래퍼 상상할 수있는 MaybeAlternative예입니다 권리 대신 -biased 왼쪽 -biased를 :a <|> Just b = Just b. 이것은 left distribution도 left catch도 만족하지 못하지만 완벽하게 유효합니다 Alternative.

그러나, 모든 종류의 이후 A는 MonadPlus그와의 인스턴스가 일치를한다고 Alternative(I이는 그것이 필요가있다 것과 같은 방식으로 필요 믿고 예 ap(<*>)대한 동일 Monad하다의 Applicative들), 당신은 정의 상상할 수있는 MonadPlus대신에 같은 클래스를

class (Monad m, Alternative m) => MonadPlus' m

클래스는 새로운 함수를 선언 할 필요가 없습니다. 그것은 순종 법률에 대한 단지 약속 empty하고 (<|>)지정된 형태. 이 디자인 기술은 Haskell 표준 라이브러리에서 사용되지 않지만 유사한 목적을 위해 좀 더 수학적인 패키지에서 사용됩니다. 예를 들어, 격자 패키지는 격자흡수 법칙에 의해 연결된 동일한 유형에 대한 결합 반격 자만나 반격 자라는 아이디어를 표현하는 데 사용합니다 .

당신이 동일한 작업을 수행 할 수없는 이유 Alternative는 보장을 원하는 경우에도, Alternative그리고 Monoid항상 일치는, 때문에 종류의 불일치이다. 원하는 클래스 선언은 다음과 같은 형식을 갖습니다.

class (Applicative f, forall a. Monoid (f a)) => Alternative''' f

그러나 (위에서 언급했듯이) GHC Haskell조차 정량화 된 제약을 지원하지 않습니다.

또한 갖는 것으로 도시 Alternative로의 슈퍼 클래스 MonadPlus요구 Applicative의 슈퍼 클래스 인 Monad행운이 그런 일이 점점 있도록. 당신이 그 문제로 실행하면 항상 거기에 WrappedMonad어떤 변 newtype은, MonadApplicative명백한 방법은; 거기에 instance MonadPlus m => Alternative (WrappedMonad m) where ...당신이 기대했던 정확하게 수행한다.


import Data.Monoid
import Control.Applicative

Monoid 및 Alternative가 Maybe펑터 및 펑터와 상호 작용하는 방법에 대한 예를 추적 해 보겠습니다 ZipList.하지만 처음부터 시작하여 부분적으로는 모든 정의를 우리 마음에 새롭고 부분적으로는 항상 탭을 약간의 해킹으로 전환하는 것을 멈추도록하겠습니다. 주로이 과거 ghci실행하여 오타를 수정할 수 있습니다 !

(<>) :: Monoid a => a -> a -> a
(<>) = mappend -- I'll be using <> freely instead of `mappend`.

다음은 Maybe 클론입니다.

data Perhaps a = Yes a | No  deriving (Eq, Show)

instance Functor Perhaps where
   fmap f (Yes a) = Yes (f a)
   fmap f No      = No

instance Applicative Perhaps where
   pure a = Yes a
   No    <*> _     = No
   _     <*> No    = No
   Yes f <*> Yes x = Yes (f x)

그리고 이제 ZipList :

data Zip a = Zip [a]  deriving (Eq,Show)

instance Functor Zip where
   fmap f (Zip xs) = Zip (map f xs)

instance Applicative Zip where
   Zip fs <*> Zip xs = Zip (zipWith id fs xs)   -- zip them up, applying the fs to the xs
   pure a = Zip (repeat a)   -- infinite so that when you zip with something, lengths don't change

구조 1 : 요소 결합 : 모노 이드

아마도 복제

먼저 Perhaps String. 두 가지를 결합하는 방법이 있습니다. 먼저 연결

(<++>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <++> Yes ys = Yes (xs ++ ys)
Yes xs <++> No     = Yes xs
No     <++> Yes ys = Yes ys
No     <++> No     = No

연결은 본질적으로 실제 Perhaps 수준이 아닌 String 수준에서 작동하는 No것처럼 처리 합니다 Yes []. 그것은 liftA2 (++). 합리적이고 유용하지만, 사용 ++하는 것에서 어떤 방식 으로든 결합하는 방법으로 일반화 할 수 있습니다 .

(<++>) :: Monoid a => Perhaps a -> Perhaps a -> Perhaps a
Yes xs <++> Yes ys = Yes (xs `mappend` ys)
Yes xs <++> No     = Yes xs
No     <++> Yes ys = Yes ys
No     <++> No     = No

이 모노 이드 구조 Perhapsa레벨 에서 가능한 한 많이 작동하려고 합니다. Monoid a우리가 a레벨의 구조를 사용하고 있음을 알려주 제약에 주목하십시오 . 이것은 Alternative 구조가 아니라 파생 된 (lifted) Monoid 구조입니다.

instance Monoid a => Monoid (Perhaps a) where
   mappend = (<++>)
   mempty = No

여기에서는 데이터의 구조를 사용하여 전체에 구조를 추가했습니다. Sets 를 결합하면 Ord a대신 컨텍스트 를 추가 할 수 있습니다 .

ZipList 클론

그렇다면 요소 를 zipList와 어떻게 결합해야 할까요? 결합하는 경우이 압축은 무엇으로해야합니까?

   Zip ["HELLO","MUM","HOW","ARE","YOU?"] 
<> Zip ["this", "is", "fun"]
=  Zip ["HELLO" ? "this",   "MUM" ? "is",   "HOW" ? "fun"]

mempty = ["","","","",..]   -- sensible zero element for zipping with ?

그러나 우리는 ?. 여기서 유일한 현명한 선택은 ++. 사실 목록의 경우(<>) = (++)

   Zip [Just 1,  Nothing, Just 3, Just 4]
<> Zip [Just 40, Just 70, Nothing]
 =  Zip [Just 1 ? Just 40,    Nothing ? Just 70,    Just 3 ? Nothing]

mempty = [Nothing, Nothing, Nothing, .....]  -- sensible zero element

그러나 ?우리가 요소를 결합하는 것을 의미한다고 말하면 무엇을 사용할 수 있습니까? 따라서 Monoid의 요소 결합 연산자를 다시 사용해야합니다 <>..

instance Monoid a => Monoid (Zip a) where
   Zip as `mappend` Zip bs = Zip (zipWith (<>) as bs) -- zipWith the internal mappend
   mempty = Zip (repeat mempty)  -- repeat the internal mempty

이것은 zip을 사용하여 요소를 결합하는 유일한 현명한 방법이므로 유일하게 현명한 monoid 인스턴스입니다.

흥미롭게도 위의 Maybe 예제에서는 작동하지 않습니다. Haskell은 Ints 를 결합하는 방법을 모르기 때문에 +또는 *? 숫자 데이터에 대한 Monoid 인스턴스를 얻으려면이를 래핑 Sum하거나 Product사용할 monoid를 지정합니다.

Zip [Just (Sum 1),   Nothing,       Just (Sum 3), Just (Sum 4)] <> 
Zip [Just (Sum 40),  Just (Sum 70), Nothing]
= Zip [Just (Sum 41),Just (Sum 70), Just (Sum 3)]

   Zip [Product 5,Product 10,Product 15] 
<> Zip [Product 3, Product 4]
 =  Zip [Product 15,Product 40]

핵심

주의 모노 이드의 유형 종류가 있다는 사실 *우리가 넣을 수 있습니다 정확히 Monoid a우리는 또한 추가 할 수 있습니다 - 여기 문맥을 Eq a하거나 Ord a. Monoid에서는 원시 요소가 중요합니다. Monoid 인스턴스는 구조 내부의 데이터를 조작하고 결합 할 수 있도록 설계되었습니다 .

구조 2 : 상위 수준 선택 : 대안

선택 연산자는 비슷하지만 또한 다릅니다.

아마도 복제

(<||>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <||> Yes ys = Yes xs   -- if we can have both, choose the left one
Yes xs <||> No     = Yes xs
No     <||> Yes ys = Yes ys
No     <||> No     = No  

여기에는 연결 이 없습니다. 우리는 전혀 사용하지 않았습니다. ++이 조합은 순전히 Perhaps수준 에서 작동 하므로 형식 서명을 다음과 같이 변경하겠습니다.

(<||>) :: Perhaps a -> Perhaps a -> Perhaps a
Yes xs <||> Yes ys = Yes xs   -- if we can have both, choose the left one
Yes xs <||> No     = Yes xs
No     <||> Yes ys = Yes ys
No     <||> No     = No  

제약이 없습니다. a레벨 의 구조를 사용하지 않고 레벨 의 구조 만 사용합니다 Perhaps. 이것은 대안 구조입니다.

instance Alternative Perhaps where
   (<|>) = (<||>)
   empty = No  

ZipList 클론

두 개의 ziplist 중에서 어떻게 선택해야합니까?

Zip [1,3,4] <|> Zip [10,20,30,40] = ????

<|>요소 에 사용 하는 것은 매우 유혹적 이지만 요소의 유형을 사용할 수 없기 때문에 사용할 수 없습니다. 의가 시작하자 empty. Alternative를 정의 할 때 요소의 유형을 알지 못하기 때문에 요소를 사용할 수 없으므로 Zip []. 에 대한 왼쪽 (가급적이면 오른쪽) ID 여야 <|>하므로

Zip [] <|> Zip ys = Zip ys
Zip xs <|> Zip [] = Zip xs

다음과 같은 두 가지 합리적인 선택이 있습니다 Zip [1,3,4] <|> Zip [10,20,30,40].

  1. Zip [1,3,4] 처음이기 때문에-Maybe와 일치합니다.
  2. Zip [10,20,30,40]가장 길기 때문에- Zip []폐기되는 것과 일치합니다.

쉽게 결정할 pure x = Zip (repeat x)수 있습니다. 두 목록이 무한 할 수 있으므로 길이 비교가 끝나지 않을 수 있으므로 첫 번째 목록을 선택해야합니다. 따라서 유일한 현명한 대안 인스턴스는 다음과 같습니다.

instance Alternative Zip where
   empty = Zip []
   Zip [] <|> x = x
   Zip xs <|> _ = Zip xs

이것은 우리가 정의 할 수있는 유일한 현명한 대안입니다. Monoid 인스턴스와 얼마나 다른지 주목하세요. 요소를 엉망으로 만들 수없고 심지어 볼 수도 없었기 때문입니다.

핵심

주의 때문 Alternative종류의 생성자를 필요 * -> *가 없다 어떤 가능한 방법 추가하기 Ord aEq a또는 Monoid a상황을. Alternative는 구조 내부의 데이터에 대한 정보를 사용할 수 없습니다 . 당신은 할 수 없습니다, 당신이 싶습니다 아무리 수행 , 데이터에 아무것도 가능성이 멀리 던져 예외입니다.

요점 : Alternative와 Monoid의 차이점은 무엇입니까?

많지는 않습니다-둘 다 모노 이드이지만 마지막 두 섹션을 요약합니다.

Monoid *인스턴스를 사용하면 내부 데이터를 결합 할 수 있습니다. Alternative (* -> *)인스턴스는 불가능합니다. Monoid는 유연성을 제공하고 Alternative는 보증을 제공합니다. 종류 *(* -> *)이러한 차이의 주요 드라이버입니다. 둘 다 있으면 두 가지 종류의 작업을 모두 사용할 수 있습니다.

이것은 옳은 것이고 우리의 두 가지 맛이 모두 적절합니다. Monoid 인스턴스는 Perhaps String모든 문자를 모으는 것을 나타내며 Alternative 인스턴스는 문자열 중에서 선택을 나타냅니다.

Maybe에 대한 Monoid 인스턴스에는 아무런 문제가 없습니다 . 데이터를 결합 하여 작업을 수행하고 있습니다.
그것은, 그 일을하고 있어요 - 아마의 대체 인스턴스 아무것도 잘못있다 선택 일 사이는.

Zip 용 Monoid 인스턴스는 요소를 결합합니다. Zip의 Alternative 인스턴스는 목록 중 하나 (비어 있지 않은 첫 번째)를 선택해야합니다.

둘 다 할 수 있다는 것이 좋습니다.

응용 컨텍스트는 무엇에 사용됩니까?

선택과 적용 사이에는 약간의 상호 작용이 있습니다. 그의 질문 또는 답변 중간에 명시된 Antal SZ의 법칙을 참조 하십시오.

실용적인 관점에서 보면 Alternative는 일부 Applicative Functor가 선택하는 데 사용되는 것이기 때문에 유용합니다. 이 기능은 Applicatives에 사용되어 일반 인터페이스 클래스가 발명되었습니다. Applicative Functor는 값 (IO, Parser, Input UI 요소 등)을 생성하는 계산을 나타내는 데 적합하며 그중 일부는 실패를 처리해야합니다. 대안이 필요합니다.

Alternative는 왜 가지고 empty있습니까?

Alternative에 빈 메서드 / 멤버가 필요한 이유는 무엇입니까? 내가 틀렸을 수도 있지만, 적어도 내가 찾을 수있는 코드에서는 전혀 사용되지 않는 것 같습니다. 그리고 수업 주제와 맞지 않는 것 같습니다. 두 가지가 있고 하나를 선택해야하는 경우 '비어 있음'이 필요한 것은 무엇입니까?

그것은 왜 더하기가 0이 필요한지 묻는 것과 같습니다. 만약 당신이 무언가를 더하고 싶다면 아무것도 추가하지 않는 무언가를 갖는 것이 요점은 무엇입니까? 대답은 0은 모든 것이 추가로 회전하는 십자형 중추적 숫자입니다. 1이 곱셈에 []중요하고 목록 y=e^x에 중요합니다 (미적분에 중요). 실제로 건물을 시작하기 위해 다음과 같은 아무것도하지 않는 요소를 사용합니다.

sum = foldr (+) 0
concat = foldr (++) []
msum = foldr (`mappend`) mempty          -- any Monoid
whichEverWorksFirst = foldr (<|>) empty  -- any Alternative

MonadPlus를 Monad + Alternative로 대체 할 수 없습니까?

MonadPlus 유형 클래스의 요점은 무엇입니까? Monad와 Alternative를 모두 사용하여 모든 장점을 잠금 해제 할 수 없습니까? 그냥 버리지 않는 이유는 무엇입니까? (내가 틀렸다고 확신하지만 반례가 없습니다)

당신은 틀리지 않았습니다. 어떤 반례도 없습니다!

귀하의 흥미로운 질문에 Antal SZ, Petr Pudlák이 있으며 MonadPlus와 Applicative 간의 관계가 실제로 무엇인지 조사했습니다. 여기여기에 대한 대답 MonadPlus(왼쪽 배포 의미에서-자세한 내용은 링크를 따라가는) 모든 것이 Alternative이지만 그 반대는 아니라는 것입니다.

즉, Monad 및 MonadPlus의 인스턴스를 만들면 어쨌든 Applicative 및 Alternative 조건을 충족합니다 . 즉, MonadPlus에 대한 규칙 (왼쪽 dist 포함)을 따르는 경우 Monad를 Applicative로 만들고 Alternative를 사용했을 수도 있습니다.

그러나 MonadPlus 클래스를 제거하면 규칙을 문서화 할 수있는 적절한 위치가 제거되고 MonadPlus (기술적으로는 Maybe를 위해 수행해야하는 작업)가되지 않고 대안을 지정할 수있는 기능을 잃게됩니다. 이것들은 이론적 인 이유입니다. 실용적인 이유는 기존 코드를 깨뜨릴 수 있기 때문입니다. (이것이 Applicative도 Functor도 Monad의 수퍼 클래스가 아닌 이유이기도합니다.)

얼터너티브와 모노 이드가 같지 않나요? 얼터너티브와 모노 이드가 완전히 다르지 않나요?

'pedia는 "Alternative 유형 클래스는 monoid 구조를 가진 Applicative functor를위한 것입니다."라고 말합니다. 이해가 안가요-Alternative가 Monoid와 완전히 다른 것을 의미하지 않나요? 즉, Alternative 유형 클래스의 요점을 두 가지 중에서 선택하는 것으로 이해 한 반면 Monoids는 사물을 결합하는 것으로 이해했습니다.

Monoid와 Alternative는 합리적인 방법으로 둘에서 하나의 객체를 얻는 두 가지 방법입니다. Maths는 데이터를 선택, 결합, 혼합 또는 부 풀리는 것에 상관하지 않으므로 Alternative를 Monoid for Applicative라고합니다. 지금은 그 개념으로 집에있는 것 같지만 이제는

Alternative 및 Monoid 인스턴스가 모두있는 유형의 경우 인스턴스는 동일합니다.

나는 이것에 동의하지 않으며, Maybe와 ZipList 예제가 왜 다른지에 대해 신중하게 설명되었다고 생각합니다. 똑같다는 것은 드문 일이라고 생각합니다. 나는 이것이 적절한 경우 하나의 예, 일반 목록만을 생각할 수 있습니다. 리스트와 모노 이드의 기본적인 예이기 때문이다 ++, 또한리스트는 그래서, 요소의 불확정 선택으로 어떤 상황에서 사용되는 <|>도해야한다 ++.


요약

  • 우리는 (동일한 작업을 제공하는 인스턴스) 일부 적용 펑터에 대해 Monoid 인스턴스를 정의해야합니다.이 인스턴스는 단순히 낮은 수준의 모노 이드를 해제하는 것이 아니라 적용 가능한 펑터 수준에서 결합됩니다. 아래 예에서 오류 litvar = liftA2 mappend literal variable방송 <|>일반적으로 정의 될 수 없다 liftA2 mappend; <|>이 경우 데이터가 아닌 파서를 결합하여 작동합니다.

  • Monoid를 직접 사용했다면 인스턴스를 정의하기 위해 언어 확장이 필요합니다. Alternative언어 확장 없이도 이러한 인스턴스를 만들 수 있습니다.

예 : 파서

일부 선언을 파싱한다고 가정 해 봅시다. 필요한 모든 것을 가져옵니다.

import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>),(<*>),liftA2,empty)
import Data.Monoid
import Data.Char

유형을 구문 분석하는 방법에 대해 생각해보십시오. 우리는 단순한 것을 선택합니다.

data Type = Literal String | Variable String  deriving Show
examples = [Literal "Int",Variable "a"]

이제 리터럴 유형에 대한 파서를 작성해 보겠습니다.

literal :: Parser Type
literal = fmap Literal $ (:) <$> upper <*> many alphaNum

의미 : upper케이스 문자를 파싱 ​​한 다음 many alphaNumeric 문자 구문 분석 하고 결과를 순수 함수를 사용하여 단일 문자열로 결합합니다 (:). 그런 다음 pure 함수 Literal적용하여 Strings를 Types 바꿉니다. lower대소 문자로 시작하는 것을 제외하고는 정확히 동일한 방식으로 변수 유형을 구문 분석합니다 .

variable :: Parser Type
variable = fmap Variable $ (:) <$> lower <*> many alphaNum

그것은 훌륭하고 parseTest literal "Bool" == Literal "Bool"정확히 우리가 바랬던 것입니다.

질문 3a : 응용 프로그램의 효과와 Monoid의 동작을 결합하려는 경우 liftA2 mappend

편집 : 죄송합니다-실제로 사용하는 것을 잊었습니다 <|>!
이제 Alternative를 사용하여이 두 파서를 결합 해 보겠습니다.

types :: Parser Type
types = literal <|> variable

이것은 모든 Type : parseTest types "Int" == Literal "Bool"parseTest types "a" == Variable "a". 이것은 값이 아닌 파서를 결합 합니다 . 그것은 데이터 수준이 아닌 Applicative Functor 수준에서 작동하는 의미입니다.

그러나 우리가 시도한다면 :

litvar = liftA2 mappend literal variable

그것은 컴파일러에게 데이터 수준에서 생성 하는 두 을 결합하도록 요청하는 것 입니다. 우리는

No instance for (Monoid Type)
  arising from a use of `mappend'
Possible fix: add an instance declaration for (Monoid Type)
In the first argument of `liftA2', namely `mappend'
In the expression: liftA2 mappend literal variable
In an equation for `litvar':
    litvar = liftA2 mappend literal variable

So we found out the first thing; the Alternative class does something genuinely different to liftA2 mappend, becuase it combines objects at a different level - it combines the parsers, not the parsed data. If you like to think of it this way, it's combination at the genuinely higher-kind level, not merely a lift. I don't like saying it that way, because Parser Type has kind *, but it is true to say we're combining the Parsers, not the Types.

(Even for types with a Monoid instance, liftA2 mappend won't give you the same parser as <|>. If you try it on Parser String you'll get liftA2 mappend which parses one after the other then concatenates, versus <|> which will try the first parser and default to the second if it failed.)

Question 3b: In what way does Alternative's <|> :: f a -> f a -> f a differ from Monoid's mappend :: b -> b -> b?

Firstly, you're right to note that it doesn't provide new functionality over a Monoid instance.

Secondly, however, there's an issue with using Monoid directly: Let's try to use mappend on parsers, at the same time as showing it's the same structure as Alternative:

instance Monoid (Parser a) where
    mempty = empty
    mappend = (<|>)

Oops! We get

Illegal instance declaration for `Monoid (Parser a)'
  (All instance types must be of the form (T t1 ... tn)
   where T is not a synonym.
   Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Parser a)'

So if you have an applicative functor f, the Alternative instance shows that f a is a monoid, but you could only declare that as a Monoid with a language extension.

Once we add {-# LANGUAGE TypeSynonymInstances #-} at the top of the file, we're fine and can define

typeParser = literal `mappend` variable

and to our delight, it works: parseTest typeParser "Yes" == Literal "Yes" and parseTest typeParser "a" == Literal "a".

Even if you don't have any synonyms (Parser and String are synonyms, so they're out), you'll still need {-# LANGUAGE FlexibleInstances #-} to define an instance like this one:

data MyMaybe a = MyJust a | MyNothing deriving Show
instance Monoid (MyMaybe Int) where
   mempty = MyNothing
   mappend MyNothing x = x
   mappend x MyNothing = x
   mappend (MyJust a) (MyJust b) = MyJust (a + b)

(The monoid instance for Maybe gets around this by lifting the underlying monoid.)

Making a standard library unnecessarily dependent on language extensions is clearly undesirable.


So there you have it. Alternative is just Monoid for Applicative Functors (and isn't just a lift of a Monoid). It needs the higher-kinded type f a -> f a -> f a so you can define one without language extensions.

Your other Questions, for completeness:

  1. Why does Alternative need an empty method/member?
    Because having an identity for an operation is sometimes useful. For example, you can define anyA = foldr (<|>) empty without using tedious edge cases.

  2. what's the point of the MonadPlus type class? Can't I unlock all of its goodness by just using something as both a Monad and Alternative? No. I refer you back to the question you linked to:

Moreover, even if Applicative was a superclass of Monad, you'd wind up needing the MonadPlus class anyways, because obeying empty <*> m = empty isn't strictly enough to prove that empty >>= f = empty.

....and I've come up with an example: Maybe. I explain in detail, with proof in this answer to Antal's question. For the purposes of this answer, it's worth noting that I was able to use >>= to make the MonadPlus instance that broke the Alternative laws.


Monoid structure is useful. Alternative is the best way of providing it for Applicative Functors.


I won't cover MonadPlus because there is disagreement about its laws.


After trying and failing to find any meaningful examples in which the structure of an Applicative leads naturally to an Alternative instance that disagrees with its Monoid instance*, I finally came up with this:

Alternative's laws are more strict than Monoid's, because the result cannot depend on the inner type. This excludes a large number of Monoid instances from being Alternatives. These datatypes allow partial (meaning that they only work for some inner types) Monoid instances which are forbidden by the extra 'structure' of the * -> * kind. Examples:

  • the standard Maybe instance for Monoid assumes that the inner type is Monoid => not an Alternative

  • ZipLists, tuples, and functions can all be made Monoids, if their inner types are Monoids => not Alternatives

  • sequences that have at least one element -- cannot be Alternatives because there's no empty:

    data Seq a
        = End a
        | Cons a (Seq a)
      deriving (Show, Eq, Ord)
    

On the other hand, some data types cannot be made Alternatives because they're *-kinded:

  • unit -- ()
  • Ordering
  • numbers, booleans

My inferred conclusion: for types that have both an Alternative and a Monoid instance, the instances are intended to be the same. See also this answer.


excluding Maybe, which I argue doesn't count because its standard instance should not require Monoid for the inner type, in which case it would be identical to Alternative


I understood the point of the Alternative type class as picking between two things, whereas I understood Monoids as being about combining things.

If you think about this for a moment, they are the same.

The + combines things (usually numbers), and it's type signature is Int -> Int -> Int (or whatever).

The <|> operator selects between alternatives, and it's type signature is also the same: take two matching things and return a combined thing.

참고URL : https://stackoverflow.com/questions/13080606/confused-by-the-meaning-of-the-alternative-type-class-and-its-relationship-to

반응형