응용 프로그램은 작성하지만 모나드는 작성하지 않습니다.


110

응용 프로그램은 구성하지만 모나드는 구성하지 않습니다.

위의 진술은 무엇을 의미합니까? 그리고 언제 하나가 다른 것보다 선호됩니까?


5
이 진술은 어디서 얻었습니까? 어떤 맥락을 보는 것이 도움이 될 수 있습니다.
— fuz

@FUZxxl : 최근 트위터의 debasishg에서 여러 사람들로부터 반복해서 들었습니다.
— missingfaktor 2011-08-12

3
많은 그런 것을 참고 : @stephen 테 틀리 Applicative의 실제로 전체하다 가족 의 Monad의 가능한 구조의 각 "형태"에 대한, 즉 하나. ZipList하지 않는 것입니다 Monad,하지만 ZipList고정 된 길이를들 수 있습니다. Reader"구조"의 크기가 환경 유형의 카디널리티로 고정되어있는 편리한 특수 (또는 일반입니까?) 경우입니다.
— CA McCann 2011 년

3
@CAMcCann Reader동형까지 모나드에 해당하는 방식으로 모양을 수정하는 경우 이러한 모든 기발한 응용 프로그램 (잘림 또는 패딩 여부)은 모나드로 제한됩니다 . 컨테이너의 모양을 수정하면 메모 트리와 같은 위치에서 함수를 효과적으로 인코딩합니다. Peter Hancock은 이러한 펑터를 "Naperian"이라고 부릅니다. 그들은 로그의 법칙을 따르기 때문입니다.
— pigworker 2011-08-12

4
@stephen tetley : 다른 예로는 상수 모노 노이드 적용 (모나드의 구성이지만 모나드가 아님)과 단위 지연 적용 (조인을 허용하지 않는 것이 더 좋음)이 있습니다.
— pigworker 2011-08-12

답변:


115

유형을 비교하면

(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m =>       m s -> (s -> m t) -> m t

우리는 두 개념을 구분하는 단서를 얻습니다. 즉, (s -> m t)유형에서 (>>=)의 값은 프로그램 s의 연산의 동작을 결정할 수있다 m t. 모나드는 값 레이어와 계산 레이어 간의 간섭을 허용합니다. (<*>)운영자는 이러한 간섭을 허용하지 : 함수 인수 및 계산 값에 의존하지 않는다. 이거 진짜 물다. 비교

miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
  b <- mb
  if b then mt else mf

어떤 효과의 결과를 사용하여 두 계산 (예 : 미사일 발사 및 휴전 협정 서명) 사이를 결정 하는 반면

iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
  cond b t f = if b then t else f

의 값을 사용하여 두 계산 의 값ab 중에서 선택 하고ataf 아마도 비극적 효과 모두 실시한.

모나 딕 버전은 본질적으로 (>>=)값에서 계산을 선택하는 추가 능력에 의존하며 이는 중요 할 수 있습니다. 그러나 그 힘을 지원하면 모나드를 구성하기가 어렵습니다. '이중 바인딩'을 구축하려고하면

(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???

여기까지 왔지만 이제 레이어가 모두 뒤죽박죽이되었습니다. 우리는 n (m (n t)), 그래서 우리는 바깥 쪽을 제거해야합니다 n. Alexandre C가 말했듯이 적절한

swap :: n (m t) -> m (n t)

순열하는 n안쪽과 join다른에 다시n .

약한 '이중 적용'은 정의하기가 훨씬 쉽습니다.

(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs

레이어간에 간섭이 없기 때문입니다.

그에 따라 Monads 의 추가 능력이 정말로 필요한 때와 Applicative지원 하는 견고한 계산 구조에서 벗어날 수있는 때 를 인식하는 것이 좋습니다 .

참고로, 모나드를 작성하는 것은 어렵지만 필요한 것보다 많을 수 있습니다. 유형 m (n v)은 m-effects로 계산 한 다음 n-effects로 v-value 로 계산하는 것을 나타냅니다 . 여기서 m-effects는 n-effects가 시작 되기 전에 완료됩니다 (따라서 swap). m-effects와 n-effects 를 인터리브하고 싶다면 , 구성은 아마도 너무 많은 질문 일 것입니다!


3
iffy 예의 경우 "ab의 값을 사용하여 at과 af의 두 계산 값 중에서 선택합니다. 둘 다 수행하여 비극적 인 결과를 초래했습니다." Haskell의 게으른 성격이 이것으로부터 당신을 보호하지 않습니까? list = (\ btf-> if b then t else f) : [] 다음 명령문을 실행합니다. list <*> pure True <*> pure "hello"<*> pure (error "bad"). ... "hello"가 표시되고 오류가 발생하지 않습니다. 이 코드는 모나드만큼 안전하거나 제어되지는 않지만 게시물은 응용 프로그램이 엄격한 평가를 유발한다고 제안하는 것처럼 보입니다. 그래도 전반적으로 훌륭한 게시물! 감사!
— shj

7
여전히 둘 다 의 효과 를 얻을 수 있지만 순수 (오류 "나쁨")에는 아무런 영향이 없습니다. 반면에 iffy (순수 True) (순수 "hello") (오류 "bad")를 시도하면 miffy가 피하는 오류가 발생합니다. 또한 iffy (순수 True) (순수 0) [1,2]와 같은 것을 시도하면 [0] 대신 [0,0]이 표시됩니다. 응용 프로그램은 고정 된 계산 시퀀스를 구축한다는 점에서 일종의 엄격함을 가지고 있지만 이러한 계산의 결과 값 은 여전히 ​​느리게 결합됩니다.
— pigworker

그것은 어떤 모나드에 대한 것으로, 사실인가요 m그리고 n당신은 항상 모나드 변압기를 쓸 수 mt및 작동 n (m t)사용 mt n t? 그래서 당신은 항상 모나드를 작성할 수 있습니다. 트랜스포머를 사용하면 더 복잡합니까?
— ron

4
그러한 변압기는 종종 존재하지만 내가 아는 한이를 생성하는 표준적인 방법은 없습니다. 다른 모나드에서 인터리브 효과를 해결하는 방법에 대한 진정한 선택이 종종 있습니다. 전형적인 예는 예외와 상태입니다. 예외 롤백 상태가 변경되어야합니까? 두 가지 선택 모두 제자리가 있습니다. 하지만 "임의의 인터리빙"을 표현하는 "무료 모나드"가 있습니다. data Free f x = Ret x | Do (f (Free f x)), 그런 다음 data (:+:) f g x = Inl (f x) | Tnr (g x)고려 Free (m :+: n). 이는 인터리빙을 실행하는 방법의 선택을 지연시킵니다.
— pigworker

@pigworker 게으른 / 엄격한 논쟁에 대해. 응용 프로그램을 사용 하면 계산 내 에서 효과를 제어 할 수 없지만 효과 레이어 는 나중에 값을 평가하지 않기로 결정할 수 있습니다. (적용 적) 파서의 경우 이는 파서가 조기에 실패하면 후속 파서가 입력에 평가 / 적용되지 않음을 의미합니다. 내용 Maybe이 수단 이른 해당 Nothing의 평가 억제 할 a나중에 / 이후의이 Just a. 이 올바른지?
— ziggystar

75

응용 프로그램은 구성하지만 모나드는 구성하지 않습니다.

모나드 는 작성하지만 결과는 모나드가 아닐 수 있습니다. 반대로 두 가지 응용 프로그램의 구성은 반드시 응용 프로그램입니다. 원래 진술의 의도는 "적용 성은 구성하지만 모나드는 구성하지 않는다"는 것이 었습니다. " Applicative는 작성 중에 닫히고 닫혀 Monad있지 않습니다."


24
추가로, 두 응용 프로그램은 완전히 기계적 방식으로 구성되는 반면, 두 모나드의 구성에 의해 형성된 모나드는 해당 구성에 따라 다릅니다.
— Apocalisp

12
더욱이 모나드는 다른 방식으로 구성됩니다. 두 모나드의 곱은 모나드입니다. 일종의 분배 법칙을 필요로하는 부산물 일뿐입니다.
— Edward KMETT 2011 년

@Apocalisp, 주석이 포함되어 있으면 이것이 가장 간결하고 가장 간결한 답변입니다.
— 폴 드레이퍼

39

당신은 applicatives있는 경우 A1와 A2, 그 유형을data A3 a = A3 (A1 (A2 a)) 도 실용적이다 (당신은 일반적인 방법에서 이러한 경우를 쓸 수 있습니다).

반면에 모나드가 M1있고 M2유형 data M3 a = M3 (M1 (M2 a))이 반드시 모나드가 아니라면 ( >>=또는에 대한 합리적인 제네릭 구현이 없습니다.join 구성에 대한이).

한 가지 예는 유형일 수 있습니다 [Int -> a](여기서는 모두 모나드 인를 []사용 하여 유형 생성자 를 구성합니다 (->) Int). 쉽게 쓸 수 있습니다

app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x

그리고 그것은 모든 응용에 일반화됩니다.

app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)

그러나 합리적인 정의가 없습니다.

join :: [Int -> [Int -> a]] -> [Int -> a]

확신이 서지 않는다면 다음 식을 고려하십시오.

join [\x -> replicate x (const ())]

반환 된 목록의 길이는 정수가 제공되기 전에 설정되어야하지만 올바른 길이는 제공된 정수에 따라 다릅니다. 따라서이 join유형에 대해 올바른 기능이 존재할 수 없습니다 .


1
... 함수가 할 때 모나드를 피합니까?
— 앤드류 쿡 2011-08-12

2
@andrew, 만약 당신이 functor를 의미했다면, yes, functor는 더 간단하고 충분할 때 사용해야합니다. 항상 그런 것은 아닙니다. 예를 들어 IOa Monad가 없으면 프로그래밍하기가 매우 어렵습니다. :)
— Rotsor

17

불행히도 우리의 진정한 목표 인 모나드 구성은 다소 어렵습니다. .. 사실, 우리는 실제로 어떤 의미에서 두 모나드의 연산만을 사용하여 위의 유형으로 조인 함수를 구성 할 수있는 방법이 없음을 실제로 증명할 수 있습니다 (증명에 대한 개요는 부록 참조). 우리가 컴포지션을 구성하는 유일한 방법은 두 구성 요소를 연결하는 몇 가지 추가 구성이있는 경우입니다.

모나드 작성, http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf


4
TL은, 참을성이 독자에 대한 DR은 : (? F)를 사용하면 자연 변환 제공 할 수있는 경우는 모나드를 구성 할 수swap : N M a -> M N a
— 알렉산더 C.

@Alexandre C .: "만약"이라고 생각합니다. 모든 모나드 변환기가 직접 펑터 구성으로 설명되는 것은 아닙니다. 예를 들어, ContT r m a없는하지도 m (Cont r a)않고 Cont r (m a), 및 StateT s m a약이다 Reader s (m (Writer s a)).
— CA McCann 2011 년

@CA McCann : (M 모나드, N 모나드, MN 모나드, NM 모나드)에서 (스왑이 있습니다 : MN-> NM 자연)으로 갈 수없는 것 같습니다. 그래서 지금은 "만일"에의 스틱을하자 (아마 대답은 종이에, 내가 고백해야 나는 빨리 그것을 보았다)
— 알렉산더 C.을

1
@Alexandre C .: 컴포지션을 모나드로 지정하는 것만으로는 충분하지 않을 수 있습니다. 두 부분을 전체와 연결하는 방법도 필요합니다. 의 존재는 swap그 구성이 두 사람이 어떻게 든 "협력"할 수 있도록 함을 의미합니다. 또한 sequence일부 모나드에 대한 "스왑"의 특수한 경우입니다. 그래서입니다 flip사실.
— CA McCann 2011 년

7
글을 쓰려면 (적절하게 ped)를 swap :: N (M x) -> M (N x)사용 하여 앞면과 뒷면에 를 삽입하고에서을 (를) 사용 하여 . returnsfmapMNN (M x) -> M (N (M (N x)))joinM (N x)
— pigworker 2011-08-12

7

분배 법칙 솔루션 l : MN-> NM이면 충분합니다.

NM의 monadicity를 보장합니다. 이것을 보려면 유닛과 멀티가 필요합니다. 나는 다중에 초점을 맞출 것이다 (단위는 unit_N unitM)

NMNM - l -> NNMM - mult_N mult_M -> NM

이것은하지 않습니다 MN이 모나드임을 보장 .

그러나 배분 법 솔루션이있을 때 중요한 관찰이 작용합니다.

l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN

따라서 LM, LN 및 MN은 모나드입니다. LMN이 모나드인지 여부에 대한 질문이 발생합니다.

(MN) L-> L (MN) 또는 N (LM)-> (LM) N

우리는 이러한지도를 만들기에 충분한 구조를 가지고 있습니다. 그러나 Eugenia Cheng이 관찰 한 바와 같이 , 우리는 양-백스터 방정식의 표현에 해당하는 육각형 조건이 필요합니다. 실제로 육각형 조건에서는 두 개의 다른 모나드가 일치합니다.


9
이것은 아마 좋은 대답이지만, 그것은 갔다 붕 내 머리 위에 방법을.
— Dan Burton

1
왜냐하면 Applicative와 haskell 태그라는 용어를 사용하면 이것은 haskell에 대한 질문이지만 다른 표기법으로 대답하기 때문입니다.
— codeshot
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.