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


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있는 경우 A1A2, 그 유형을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.