[haskell] 모나드는 무엇입니까?

최근 Haskell을 간략히 살펴보면 모나드가 무엇인지에 대한 간결하고 간결하며 실용적인 설명은 무엇입니까?

내가 접한 대부분의 설명은 접근하기가 어렵고 실제적인 세부 사항이 부족하다는 것을 알았습니다.



답변

첫째 : 모나드 라는 용어 는 수학자가 아니라면 약간 공허합니다. 다른 용어는 실제로 유용한 것에 대해 좀 더 설명하는 계산 빌더 입니다.

실용적인 예를 요청하십시오.

예 1 : 목록 이해 :

[x*2 | x<-[1..10], odd x]

이 표현식은 1에서 10 사이의 모든 홀수의 배가를 반환합니다. 매우 유용합니다!

이것은 List 모나드 내의 일부 연산에 대해 실제로 구문 설탕이라는 것이 밝혀졌습니다. 동일한 목록 이해는 다음과 같이 쓸 수 있습니다.

do
   x <- [1..10]
   guard (odd x)
   return (x * 2)

또는:

[1..10] >>= (\x -> guard (odd x) >> return (x*2))

예 2 : 입력 / 출력 :

do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Welcome, " ++ name ++ "!")

두 예제 모두 AKA 계산 빌더 인 모나드를 사용합니다. 일반적인 주제는 모나드가 특정적이고 유용한 방식으로 작업연결 한다는 것입니다. 리스트 이해에서, 조작이리스트를 리턴하면리스트의 모든 항목대해 다음 조작이 수행되도록 조작이 연결 됩니다. 반면에 IO 모나드는 순차적으로 작업을 수행하지만 “세계 상태”를 나타내는 “숨겨진 변수”를 전달하여 순수한 기능 방식으로 I / O 코드를 작성할 수 있습니다.

체인 작업 의 패턴 은 매우 유용하며 Haskell의 많은 다른 것들에 사용됩니다.

또 다른 예는 예외입니다. Error모나드를 사용하면 오류가 발생하는 경우를 제외하고는 체인의 나머지 부분을 버리는 경우를 제외하고 작업이 순차적으로 수행되도록 체인이 연결됩니다.

리스트 이해 구문과 do-notation은 모두 >>=연산자를 사용하여 연쇄 연산을위한 구문 설탕입니다 . 모나드는 기본적으로 >>=연산자 를 지원하는 유형입니다 .

예 3 : 파서

이것은 인용 된 문자열이나 숫자를 구문 분석하는 매우 간단한 파서입니다.

parseExpr = parseString <|> parseNumber

parseString = do
        char '"'
        x <- many (noneOf "\"")
        char '"'
        return (StringValue x)

parseNumber = do
    num <- many1 digit
    return (NumberValue (read num))

작업은 char, digit, 등 매우 간단합니다. 일치하거나 일치하지 않습니다. 마술은 제어 흐름을 관리하는 모나드입니다. 일치가 실패 할 때까지 작업이 순차적으로 수행됩니다.이 경우 모나드는 최신으로 되돌아가 <|>다음 옵션을 시도합니다. 또 다른 유용한 의미 체계를 사용하여 작업을 연결하는 방법입니다.

예 4 : 비동기 프로그래밍

위의 예제는 Haskell에 있지만 F # 은 모나드도 지원합니다. 이 예는 Don Syme 에서 도난당했습니다 .

let AsyncHttp(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             return reader.ReadToEnd() }

이 메소드는 웹 페이지를 가져옵니다. 펀치 라인을 사용합니다 GetResponseAsync-실제로는 별도의 스레드에서 응답을 기다리는 동안 주 스레드는 함수에서 돌아옵니다. 응답이 수신되면 생성 된 스레드에서 마지막 세 줄이 실행됩니다.

대부분의 다른 언어에서는 응답을 처리하는 행에 대해 별도의 함수를 명시 적으로 작성해야합니다. async모나드 자체에 블록 “분할”할 수 있고 후반의 실행을 연기. ( async {}구문은 블록의 제어 흐름이 async모나드에 의해 정의됨을 나타냅니다 .)

작동 방식

그렇다면 모나드는이 모든 멋진 제어 흐름을 어떻게 수행 할 수 있습니까? 실제로 do-block (또는 F #에서 호출 되는 계산 식 )에서 발생하는 것은 모든 작업 (기본적으로 모든 행)이 별도의 익명 함수로 래핑된다는 것입니다. 이 함수는 bind연산자를 사용하여 결합됩니다 ( >>=Haskell에서 철자 ). bind연산은 기능을 결합 하기 때문에 순차적으로, 여러 번, 반대로, 일부를 버리고, 느낌이들 때 별도의 스레드에서 일부를 실행하는 것처럼 적절하다고 생각되면 실행할 수 있습니다.

예를 들어, 이것은 예제 2의 확장 된 IO 코드 버전입니다.

putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))

이것은 더 나쁘지만 실제로 진행되고있는 것이 더 분명합니다. >>=연산자 마법 성분이다 : 또한, (우측) 함수와 (왼쪽) 값을 결합하여 얻어 새로운 값을 생성한다. 이 새로운 값은 다음 >>=연산자에 의해 다시 취해지고 새로운 값을 생성하는 함수와 다시 결합됩니다. >>=미니 평가자로 볼 수 있습니다.

참고 >>=모든 모나드는 자체 구현이 있으므로, 다른 종류의 오버로드 >>=. (체인의 모든 작업은 동일한 모나드 유형이어야 >>=합니다. 그렇지 않으면 작업자가 작동하지 않습니다.)

가능한 가장 간단한 구현은 >>=왼쪽의 값을 가져 와서 오른쪽의 함수에 적용하고 결과를 반환하지만 이전에 언급했듯이 모나드의 구현에 추가로 진행되는 일이있을 때 전체 패턴을 유용하게 만드는 것은 >>=.

값이 한 작업에서 다음 작업으로 전달되는 방식에는 약간의 영리함이 있지만 Haskell 유형 시스템에 대한 자세한 설명이 필요합니다.

합산

Haskell-terms에서 모나드는 모나드 타입 클래스의 인스턴스 인 매개 변수화 된 타입 >>=으로, 몇몇 다른 연산자와 함께 정의 됩니다. 평신도의 관점에서 모나드는 >>=작업이 정의 된 유형일뿐 입니다.

그 자체로 >>=는 귀찮은 연쇄 함수 방식이지만 “배관”을 숨기는 do-notation의 존재로 monadic 연산은 매우 훌륭하고 유용한 추상화이며 언어의 많은 곳에서 유용하며 유용합니다. 언어로 자신의 미니 언어를 만들 수 있습니다.

모나드는 왜 어려운가요?

많은 Haskell-learners에게 모나드는 벽돌 벽처럼 치는 장애물입니다. 모나드 자체가 복잡하지는 않지만 구현은 매개 변수화 된 유형, 유형 클래스 등과 같은 다른 많은 고급 Haskell 기능에 의존합니다. 문제는 Haskell I / O가 모나드를 기반으로한다는 것이며, I / O는 아마도 새로운 언어를 배울 때 가장 먼저 이해하고 싶을 것입니다. 결국, 전혀 생산하지 않는 프로그램을 만드는 것은 그리 재미 있지 않습니다. 산출. 다른 언어 부분에 대한 경험이 충분할 때까지 I / O를 “매직 발생”과 같이 처리하는 것을 제외하고는이 닭과 계란 문제에 대한 즉각적인 해결책은 없습니다. 죄송합니다.

모나드에 대한 훌륭한 블로그 :
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html


답변

“모나드 란 무엇인가”를 설명하는 것은 “숫자는 무엇입니까?” 우리는 항상 숫자를 사용합니다. 그러나 숫자에 대해 전혀 모르는 사람을 만났다고 상상해보십시오. 어떻게 도대체 당신은 숫자가 무엇인지 설명 할 것인가? 그리고 왜 그것이 유용한 지 설명하기 시작 하시겠습니까?

모나드는 무엇입니까? 짧은 대답 : 그것은 작업을 연결하는 구체적인 방법입니다.

본질적으로 실행 단계를 작성하고이를 “바인드 기능”과 연결합니다. (Haskell에서 이름은으로 지정 >>=됩니다.) 바인드 연산자에 직접 호출을 작성하거나 컴파일러가 해당 함수 호출을 삽입하게하는 구문 sugar를 사용할 수 있습니다. 그러나 어느 쪽이든, 각 단계는이 바인드 함수에 대한 호출로 분리됩니다.

따라서 바인드 함수는 세미콜론과 같습니다. 프로세스의 단계를 분리합니다. 바인드 기능의 작업은 이전 단계에서 출력을 가져 와서 다음 단계로 공급하는 것입니다.

너무 힘들게 들리지 않습니까? 그러나 하나 이상의 모나드가 있습니다. 왜? 어떻게?

바인드 함수 한 단계에서 결과를 가져 와서 다음 단계로 전달할 수 있습니다. 그러나 그것이 “모두”라면 모나드는 그렇습니다 … 실제로는 그다지 유용하지 않습니다. 이해하는 것이 중요합니다. 모든 유용한 모나드는 모나드 일뿐 아니라 다른 일도합니다 . 모든 유용한 모나드는 “특별한 힘”을 가지고있어 독특합니다.

( 특별히 아무 것도 하지 않는 모나드 는 “identity monad”라고 불립니다. 신원 기능과는 달리이 기능은 전혀 무의미한 것처럼 들리지만 그렇지는 않습니다 … 그러나 그것은 또 다른 이야기입니다.)

기본적으로 각 모나드는 자체 바인딩 기능을 구현합니다. 그리고 실행 단계 사이에서 후프를 수행하도록 바인드 함수를 작성할 수 있습니다. 예를 들면 다음과 같습니다.

  • 각 단계가 성공 / 실패 표시기를 리턴하면, 이전 단계가 성공한 경우에만 다음 단계를 바인드하도록 바인드 할 수 있습니다. 이런 식으로 실패한 단계는 조건부 테스트없이 전체 시퀀스를 “자동으로”중단합니다. ( 실패 모나드 .)

  • 이 아이디어를 확장하면 “예외”를 구현할 수 있습니다. ( Error Monad 또는 Exception Monad .) 언어 기능이 아니라 직접 정의하기 때문에 작동 방식을 정의 할 수 있습니다. (예를 들어, 처음 두 예외를 무시하고 세 번째 예외가 발생할 때만 중단하려고 할 수 있습니다 .)

  • 각 단계에서 여러 개의 결과를 반환하도록 하고 bind 함수가 그 결과를 반복하도록하여 각 단계를 다음 단계로 전달할 수 있습니다. 이러한 방식으로 여러 결과를 처리 할 때 여러 곳에 루프를 계속 쓸 필요가 없습니다. 바인드 기능은 “자동으로”모든 기능을 수행합니다. ( 목록 Monad .)

  • 한 단계에서 다른 단계로 “결과”를 전달하는 것 외에도 바인드 함수 가 추가 데이터전달 하도록 할 수 있습니다 . 이 데이터는 이제 소스 코드에 표시되지 않지만 수동으로 모든 함수에 전달하지 않고도 어디서나 액세스 할 수 있습니다. ( 독자 모나드 .)

  • “추가 데이터”를 교체 할 수 있습니다. 이를 통해 실제로 파괴적인 업데이트를 수행하지 않고도 파괴적인 업데이트시뮬레이션 할 수 있습니다 . ( 주 모나드 와 그 사촌 작가 모나드 .)

  • 파괴적인 업데이트 만 시뮬레이션 하기 때문에 실제 파괴적인 업데이트 로는 불가능한 일을 간단하게 수행 할 수 있습니다. 예를 들어 마지막 업데이트를 실행 취소 하거나 이전 버전으로 되돌릴 수 있습니다.

  • 계산을 일시 중지 할 수있는 모나드를 만들 수 있으므로 프로그램을 일시 중지하고 내부 상태 데이터로 들어가서 땜질 한 다음 다시 시작할 수 있습니다.

  • “연속”을 모나드로 구현할 수 있습니다. 이를 통해 사람들의 마음아프게 할 수 있습니다 !

이 모든 것이 모나드로 가능합니다. 물론이 모든 것도 모나드 없이도 완벽하게 가능합니다 . 그것은 크게 그냥 쉽게 모나드를 사용하여.


답변

사실, 모나드에 대한 일반적인 이해와는 달리, 국가와는 아무런 관련이 없습니다. 모나드는 단순히 물건을 포장하는 방법이며 포장을 풀지 않고 포장 된 물건에 대한 작업을 수행하는 방법을 제공합니다.

예를 들어 Haskell에서 다른 유형을 감싸는 유형을 만들 수 있습니다.

data Wrapped a = Wrap a

우리가 정의한 것을 감싸기 위해

return :: a -> Wrapped a
return x = Wrap x

풀기없이 작업을 수행하려면 함수가 있다고 가정 해 f :: a -> b다음이 작업을 수행 할 수 있습니다, 들어 포장 값에 따라 행동하는 그 기능을 :

fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)

이해해야 할 모든 것입니다. 그러나,이 할 수있는 일반적인 기능이 있음을 밝혀 리프팅 입니다 bind:

bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x

bind보다 조금 더 할 수 fmap있지만 그 반대는 아닙니다. 실제로 및 fmap로만 정의 할 수 있습니다 . 그래서, 모나드를 정의 할 때 .. 당신은 그것의 타입 (여기서)을주고 그 동작 과 작동 방식을 말합니다 .bindreturnWrapped areturnbind

멋진 점은 이것이 일반적인 패턴으로 판명되어 순수한 방식으로 상태를 캡슐화하는 것은 그중 하나 일 뿐이라는 것입니다.

Haskell의 IO 모나드에서 사용되는 것과 같이 모나드를 사용하여 기능적 종속성을 도입하고 평가 순서를 제어하는 ​​방법에 대한 좋은 기사를 보려면 IO Inside를 확인하십시오 .

모나드를 이해하는 것에 대해서는 너무 걱정하지 마십시오. 당신이 흥미로운 것을 발견하고 바로 이해하지 않아도 걱정하지 마십시오. 그렇다면 Haskell과 같은 언어로 다이빙하는 것이 좋습니다. 모나드는 연습을 통해 뇌 속으로 간계를 이해하는 순간, 언젠가 갑자기 이해한다는 사실을 깨닫게됩니다.


답변

그러나, 당신은 Monads를 발명했을 수 있습니다!

sigfpe 말한다 :

그러나이 모든 것은 모나드를 설명이 필요한 난해한 것으로 소개합니다. 그러나 내가 주장하고 싶은 것은 전혀 비전 적이 지 않다는 것입니다. 실제로 함수형 프로그래밍에서 다양한 문제에 직면했을 때 모나드의 예인 특정 솔루션으로 이어질 수밖에 없었습니다. 사실, 아직 개발하지 않았다면 지금 발명하도록하겠습니다. 그런 다음 이러한 모든 솔루션이 실제로 동일한 솔루션이라는 것을 알 수있는 작은 단계입니다. 이 글을 읽은 후에는 이미 발명 한 것으로 보이는 모든 것을 인식 할 수 있기 때문에 모나드에 대한 다른 문서를 이해하기에 더 나은 위치에있을 수 있습니다.

모나드가 해결하려고하는 많은 문제는 부작용 문제와 관련이 있습니다. 먼저 시작하겠습니다. 모나드를 사용하면 부작용을 처리하는 것 이상을 수행 할 수 있습니다. 특히 많은 유형의 컨테이너 객체를 모나드로 볼 수 있습니다. 다른 사람.)

C ++과 같은 명령형 프로그래밍 언어에서 함수는 수학 함수와 같이 동작하지 않습니다. 예를 들어, 단일 부동 소수점 인수를 사용하고 부동 소수점 결과를 리턴하는 C ++ 함수가 있다고 가정하십시오. 피상적으로는 실수를 실수에 매핑하는 수학 함수처럼 보일 수 있지만 C ++ 함수는 인수에 따라 숫자를 반환하는 것 이상을 수행 할 수 있습니다. 전역 변수의 값을 읽고 쓸 수있을뿐만 아니라 화면에 출력을 기록하고 사용자로부터 입력을받을 수 있습니다. 그러나 순수한 기능적 언어에서 함수는 인수로 제공된 내용 만 읽을 수 있으며 세상에 영향을 줄 수있는 유일한 방법은 반환되는 값을 통하는 것입니다.


답변

모나드는 >>=(aka bind) 및 return(aka unit)의 두 가지 연산이있는 데이터 유형입니다 . return임의의 값을 가져와 모나드 인스턴스를 만듭니다. >>=모나드의 인스턴스를 가져 와서 그 위에 함수를 매핑합니다. (모나드는 대부분의 프로그래밍 언어에서 임의의 값을 취하고 그로부터 유형을 생성하는 함수를 작성할 수 없기 때문에 모나드는 이상한 종류의 데이터 유형임을 이미 알 수 있습니다. 모나드는 일종의 파라 메트릭 다형성을 사용 합니다.)

Haskell 표기법에서 모나드 인터페이스가 작성됩니다

class Monad m where
  return :: a -> m a
  (>>=) :: forall a b . m a -> (a -> m b) -> m b

이러한 작업은 특정 “법률”을 준수하기로되어 있지만,이 굉장히 중요하지 같습니다 “법률”단지 작업의 방법 분별 구현은 기본적으로 (행동한다고 성문화, 그 >>=와는 return값이 모나드 인스턴스와로 변환 얻을 방법에 대해 동의한다고 그것은 >>=연관성이 있습니다).

모나드는 상태와 I / O에 관한 것이 아니라 상태, I / O, 예외 및 비결정론에 대한 작업을 포함하는 일반적인 계산 패턴을 추상화합니다. 아마도 가장 간단한 모나드는 목록과 옵션 유형입니다.

instance Monad [ ] where
    []     >>= k = []
    (x:xs) >>= k = k x ++ (xs >>= k)
    return x     = [x]

instance Monad Maybe where
    Just x  >>= k = k x
    Nothing >>= k = Nothing
    return x      = Just x

여기서 []:목록 생성자이며, ++연결 연산자이며, JustNothing이다 Maybe생성자. 이 모나드는 둘 다 공통적이고 유용한 계산 패턴을 각 데이터 유형에 캡슐화합니다 (부작용이나 I / O와 관련이 없음).

모나드가 무엇인지, 왜 유용한 지 이해하려면 사소하지 않은 Haskell 코드를 작성해야합니다.


답변

먼저 functor가 무엇인지 이해해야합니다. 그 전에 고차 함수를 이해하십시오.

고차 함수는 단순히 인자로서의 기능을 취하는 함수이다.

펑는 임의의 타입 구조이다 T고차 함수가 존재하는, 호출 map, 그 변환 타입의 함수 a -> b(임의의 두 유형을 지정 a하고 b함수로) T a -> T b. 이 map함수는 또한 정체성과 구성의 법칙을 준수하여 다음 표현이 모두에게 적용되도록 p하고 q(Haskell 표기법) :

map id = id
map (p . q) = map p . map q

예를 들어, 위의 법칙을 준수하는 List유형의 함수가 장착 된 경우 , 유형 생성자라는 함수는 functor (a -> b) -> List a -> List b입니다. 실질적인 구현은 명백합니다. 결과 List a -> List b함수는 주어진 목록을 반복 (a -> b)하여 각 요소에 대한 함수를 호출 하고 결과 목록을 리턴합니다.

모나드는 본질적으로 펑터이다 T이 추가 방법과, join입력의 T (T a) -> T a, 그리고 unit(라고도 return, fork또는 pure유형) a -> T a. Haskell의 목록 :

join :: [[a]] -> [a]
pure :: a -> [a]

왜 유용한가요? 예를 들어, map목록을 반환하는 함수가있는 목록 위에 있을 수 있기 때문 입니다. Join결과 목록을 가져 와서 연결합니다. List이것이 가능하기 때문에 모나드입니다.

map그런 다음 함수를 작성할 수 있습니다 join. 이 기능을 bind, 또는 flatMap, 또는 (>>=), 또는이라고 (=<<)합니다. 이것은 일반적으로 Haskell에서 모나드 인스턴스가 제공되는 방식입니다.

모나드는 특정 법, 즉 관련 법을 충족시켜야 join합니다. 당신이 값이있는 경우 있음이 수단 x유형을 [[[a]]]다음 join (join x)과 같아야한다 join (map join x). 그리고 그런 pure정체성을 가져야합니다 .joinjoin (pure x) == x


답변

[면책 조항 : 나는 여전히 모나드를 완전히 구르려고합니다. 다음은 내가 지금까지 이해 한 것입니다. 그것이 틀렸다면, 잘 알고있는 누군가가 카펫 위에서 나를 부를 것입니다.]

Arnar는 다음과 같이 썼습니다.

모나드는 단순히 물건을 포장하는 방법이며 포장을 풀지 않고 포장 된 물건에 대한 작업을 수행하는 방법을 제공합니다.

바로 그거야. 아이디어는 다음과 같습니다.

  1. 당신은 어떤 종류의 가치를 취하고 그것을 추가 정보로 포장합니다. 값이 특정 종류 (예 : 정수 또는 문자열) 인 것처럼 추가 정보는 특정 종류입니다.

    예를 들어, 추가 정보는 a Maybe또는 a 일 수 있습니다 IO.

  2. 그런 다음 추가 정보를 전달하면서 랩핑 된 데이터를 조작 할 수있는 일부 연산자가 있습니다. 이 연산자는 추가 정보를 사용하여 랩핑 된 값에 대한 작업 동작을 변경하는 방법을 결정합니다.

    예를 들어 a Maybe IntJust Int또는 일 수 있습니다 Nothing. 이제 당신이 추가하면, Maybe IntA와 Maybe Int운영자들이 모두 있는지 확인합니다 Just Int내부이야, 그렇게되면, 랩을 해제 Int,의를 그들에게 추가 연산자를 통과, 결과를 재 – 포장 Int새로운으로 Just Int유효한 인 ( Maybe Int)를 반환하여 a를 반환합니다 Maybe Int. 그러나 그중 하나가 Nothing내부에 있으면이 연산자는 즉시 반환 Nothing합니다 Maybe Int. 이는 다시 유효합니다 . 그런 식으로, 당신은 Maybe Ints가 단지 정상적인 숫자 인 척하고 정규 수학을 수행 할 수 있습니다. 만약 당신이을 얻는다면 Nothing, 당신의 방정식은 여전히 모든 곳에서 점검을하지 않아도Nothing 올바른 결과를 만들어 낼 것 입니다.

그러나 그 예는 단지 일어나는 일이다 Maybe. 추가 정보가 IO이면 IOs에 대해 정의 된 해당 특수 연산자 가 대신 호출되며 추가를 수행하기 전에 완전히 다른 작업을 수행 할 수 있습니다. (좋아요, 두 개의 IO Ints를 함께 추가 하는 것은 아마도 의미가 없습니다. 아직 확실하지 않습니다.) (또한 Maybe예제에 주의를 기울인다면 “추가적인 것들로 가치를 싸는 것”이 항상 올바른 것은 아니라는 것을 알았습니다. 정확하고 정확하며 정확해야합니다.)

기본적으로 “monad”는 대략 “pattern”을 의미 합니다. 그러나 비공식적으로 설명되고 구체적으로 명명 된 Patterns로 가득 찬 책 대신, 이제 새로운 구문을 프로그램에서 물건 으로 선언 할 수 있는 언어 구성 ( 구문 및 모두)이 있습니다 . (여기서 부정확 한 점은 모든 패턴이 특정 형태를 따라야한다는 것이므로 모나드는 패턴만큼 일반적이지 않습니다. 그러나 이것이 대부분의 사람들이 알고 이해하는 가장 가까운 용어라고 생각합니다.)

이것이 사람들이 모나드를 매우 혼란스럽게 생각하는 이유입니다. 왜냐하면 그들은 일반적인 개념이기 때문입니다. 무엇을 모나드로 만드는지 묻는 것은 패턴을 만드는 것이 무엇인지 묻는 것과 유사합니다.

그러나 패턴에 대한 아이디어를 언어로 구문 적으로 지원한다는 의미를 생각해보십시오. Gang of Four 책 을 읽고 특정 패턴의 구성을 암기하는 대신 이 패턴을 불가지론 적으로 구현하는 코드를 작성 하면됩니다 . 한 번 일반적인 방법으로 완료되면! 그런 다음 방문자 나 전략 또는 Façade와 같은 패턴을 반복해서 다시 구현하지 않고도 코드의 작업을 장식하여 재사용 할 수 있습니다!

그래서 모나드 를 이해하는 사람들이 그렇게 유용하다고 생각하는 이유 는 지적 상식이 이해에 대해 자부심을 갖는 상아탑 개념은 아니지만 (물론 티히) 물론 실제로 코드를 더 단순하게 만듭니다.