[oop] 평범한 영어 모나드? (FP 배경이없는 OOP 프로그래머의 경우)

OOP 프로그래머가 (기능적 프로그래밍 배경없이) 이해한다는 관점에서 모나드는 무엇입니까?

어떤 문제를 해결하고 가장 많이 사용되는 장소는 무엇입니까?

편집하다:

내가 찾고있는 이해를 명확히하기 위해 모나드가있는 FP 응용 프로그램을 OOP 응용 프로그램으로 변환한다고 가정 해 봅시다. 모나드의 책임을 OOP 앱으로 포팅하기 위해 무엇을 하시겠습니까?



답변

업데이트 :이 질문은 상당히 긴 블로그 시리즈의 주제였습니다.이 기사는 Monads 에서 읽을 수 있습니다 . 훌륭한 질문에 감사드립니다!

OOP 프로그래머가 (기능적 프로그래밍 배경없이) 이해한다는 관점에서 모나드는 무엇입니까?

모나드는 인 유형의 “증폭” 특정 규칙을 따르는제공된 특정 작업을 갖는다 .

먼저, “타입 증폭기”란 무엇입니까? 그것은 당신이 타입을 가져 와서 더 특별한 타입으로 바꿀 수있는 시스템을 의미합니다. 예를 들어 C #에서는을 고려하십시오 Nullable<T>. 이것은 유형의 증폭기입니다. 예를 들어 유형을 가져 와서 해당 유형 int에 새로운 기능을 추가 할 수 있습니다. 즉 이전에는 불가능했을 때 null이 될 수 있습니다.

두 번째 예로,을 고려하십시오 IEnumerable<T>. 유형의 증폭기입니다. 즉, 유형을 가져 와서 해당 유형 string에 새로운 기능을 추가 할 수 있습니다. 즉, 이제는 여러 개의 단일 문자열에서 일련의 문자열을 만들 수 있습니다.

“확실한 규칙”은 무엇입니까? 간단히 말해서, 기본 유형의 기능이 정상적인 유형의 기능 구성 규칙을 따르도록 증폭 된 유형에서 작동하는 현명한 방법이 있습니다. 예를 들어 정수에 함수가 있다면

int M(int x) { return x + N(x * 2); }

그러면 해당 기능을 Nullable<int>통해 모든 연산자와 호출이 이전과 같은 방식으로 함께 작동 할 수 있습니다.

(이것은 엄청나게 모호하고 부정확합니다. 기능 구성에 대한 지식에 대해서는 아무 것도 가정하지 않은 설명을 요청했습니다.)

“작업”은 무엇입니까?

  1. 일반 유형에서 값을 가져와 동등한 모나드 값을 생성하는 “단위”연산 (혼란 적으로 “리턴”연산이라고도 함)이 있습니다. 본질적으로 이것은 증폭되지 않은 유형의 값을 가져 와서 증폭 된 유형의 값으로 바꾸는 방법을 제공합니다. OO 언어의 생성자로 구현 될 수 있습니다.

  2. 모나드 값과 값을 변환 할 수있는 함수를 가져 와서 새 모나드 값을 반환하는 “바인드”연산이 있습니다. 바인드는 모나드의 의미를 정의하는 핵심 작업입니다. 이를 통해 증폭되지 않은 유형의 연산을 증폭 된 유형의 연산으로 변환 할 수 있으며, 이는 앞에서 언급 한 기능 구성 규칙을 따릅니다.

  3. 증폭되지 않은 유형을 증폭 된 유형에서 다시 가져 오는 방법이 종종 있습니다. 엄밀히 말하면이 작업에는 모나드가 필요하지 않습니다. ( comonad 를 원한다면 필요하지만 이 기사에서는 더 이상 고려하지 않을 것이다.)

다시 한 번 Nullable<T>예를 들어 보자. 생성자를 사용하여 int로 바꿀 수 있습니다 Nullable<int>. C # 컴파일러는 널 입력 가능 “리프팅”을 처리하지만, 그렇지 않은 경우 리프팅 변환이 간단합니다.

int M(int x) { whatever }

로 변환

Nullable<int> M(Nullable<int> x) 
{ 
    if (x == null) 
        return null; 
    else 
        return new Nullable<int>(whatever);
}

그리고 재산 Nullable<int>으로 다시 돌아가는 int것은 Value재산으로 이루어집니다 .

핵심 비트 인 함수 변환입니다. Nullable 연산의 실제 의미 ( null확산 에 대한 연산) null가 변환에서 어떻게 캡처 되는지 확인합니다 . 우리는 이것을 일반화 할 수 있습니다.

당신이에서 함수가 있다고 가정 intint우리의 원래처럼 M. nullable 생성자를 통해 결과를 실행할 수 있기 때문에 intand를 반환 하는 함수로 쉽게 만들 수 있습니다 Nullable<int>. 이제 다음과 같은 고차 방법이 있다고 가정하십시오.

static Nullable<T> Bind<T>(Nullable<T> amplified, Func<T, Nullable<T>> func)
{
    if (amplified == null) 
        return null;
    else
        return func(amplified.Value);
}

당신이 그걸로 무엇을 할 수 있습니까? an int을 반환하거나 int, inta Nullable<int>를 반환하고 a 를 반환하는 모든 메서드 는 이제 nullable 의미를 적용 할 수 있습니다.

또한 : 두 가지 방법이 있다고 가정하십시오.

Nullable<int> X(int q) { ... }
Nullable<int> Y(int r) { ... }

그리고 당신은 그것들을 작성하고 싶습니다 :

Nullable<int> Z(int s) { return X(Y(s)); }

즉, Z의 조성물 XY. 때문에하지만 당신은 할 수 없어 X을 소요 int하고, Y리턴한다 Nullable<int>. 그러나 “바인드”작업이 있으므로 다음 작업을 수행 할 수 있습니다.

Nullable<int> Z(int s) { return Bind(Y(s), X); }

모나드에 대한 바인드 작업은 증폭 된 유형의 함수 구성을 작동시키는 것입니다. 위에서 언급 한 “규칙”은 모나드가 정상적인 기능 구성 규칙을 유지한다는 것입니다. 아이덴티티 함수로 구성하면 원래의 함수가되고 그 컴포지션은 연관됩니다.

C #에서 “Bind”는 “SelectMany”라고합니다. 시퀀스 모나드에서 어떻게 작동하는지 살펴보십시오. 값을 시퀀스로 바꾸고 시퀀스에 대한 연산을 바인딩하는 두 가지가 필요합니다. 보너스로, 우리는 또한 “시퀀스를 가치로 되돌려 놓았습니다”. 이러한 작업은 다음과 같습니다.

static IEnumerable<T> MakeSequence<T>(T item)
{
    yield return item;
}
// Extract a value
static T First<T>(IEnumerable<T> sequence)
{
    // let's just take the first one
    foreach(T item in sequence) return item; 
    throw new Exception("No first item");
}
// "Bind" is called "SelectMany"
static IEnumerable<T> SelectMany<T>(IEnumerable<T> seq, Func<T, IEnumerable<T>> func)
{
    foreach(T item in seq)
        foreach(T result in func(item))
            yield return result;            
}

nullable 모나드 규칙은 “nullable을 생성하는 두 함수를 결합하여 내부 함수가 null인지 확인하고, 그렇지 않으면 null을 생성하고 그렇지 않은 경우 결과와 함께 외부 함수를 호출합니다.” 이것이 원하는 nullable의 의미입니다.

시퀀스 모나드 규칙은 “서열을 생성하는 두 함수를 결합하고, 내부 함수에 의해 생성 된 모든 요소에 외부 함수를 적용한 다음 모든 결과 시퀀스를 함께 연결하는 것”입니다. 모나드의 기본 의미론은 Bind/ SelectMany메소드 에서 캡처됩니다 . 이것은 모나드가 실제로 무엇을 의미 하는지 알려주는 방법입니다 .

우리는 더 잘할 수 있습니다. int 시퀀스와 int를 가져 와서 문자열 시퀀스를 생성하는 메소드가 있다고 가정하십시오. 하나의 입력이 다른 입력의 출력과 일치하는 한 다른 증폭 유형을 가져오고 반환하는 함수의 구성을 허용하도록 바인딩 작업을 일반화 할 수 있습니다.

static IEnumerable<U> SelectMany<T,U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> func)
{
    foreach(T item in seq)
        foreach(U result in func(item))
            yield return result;            
}

이제이 개별 정수를 정수 시퀀스로 증폭합니다.이 특정 정수를 문자열 시퀀스로 증폭하고 문자열 시퀀스로 증폭합니다. 이제 두 연산을 모두 합치십시오. “문자열의 모든 순서.” Monads를 사용하면 증폭 을 구성 할 수 있습니다 .

어떤 문제를 해결하고 가장 많이 사용되는 장소는 무엇입니까?

그것은 “싱글 톤 패턴이 어떤 문제를 해결합니까?”

모나드는 일반적으로 다음과 같은 문제를 해결하는 데 사용됩니다.

  • 이 유형에 대해 새로운 기능을 만들어야하지만이 유형에 대해 이전 기능을 결합하여 새로운 기능을 사용해야합니다.
  • 유형에 대한 많은 작업을 캡처하고 해당 작업을 컴포저 블 객체로 나타내어 적절한 일련의 작업을 나타낼 때까지 더 크고 더 큰 컴포지션을 구축 한 다음 결과에서 결과를 얻어야합니다.
  • 부작용을 싫어하는 언어로 부작용 작업을 깨끗하게 표현해야합니다.

C #은 디자인에서 모나드를 사용합니다. 이미 언급했듯이, nullable 패턴은 “아마도 모나드”와 매우 유사합니다. LINQ는 전적으로 모나드로 만들어졌습니다. 이 SelectMany방법은 작업 구성의 의미 론적 작업입니다. (Erik Meijer는 모든 LINQ 기능이 실제로 구현 될 수 있다는 점을 좋아 SelectMany합니다. 다른 모든 기능은 편의에 불과합니다.)

내가 찾고있는 이해를 명확히하기 위해 모나드가있는 FP 응용 프로그램을 OOP 응용 프로그램으로 변환한다고 가정 해 봅시다. 모나드의 책임을 OOP 앱에 이식하려면 어떻게해야합니까?

대부분의 OOP 언어에는 모나드 패턴 자체를 직접 표현할 수있는 충분한 유형의 시스템이 없습니다. 일반 유형보다 유형이 높은 유형을 지원하는 유형 시스템이 필요합니다. 그래서 나는 그것을 시도하지 않을 것입니다. 오히려 각 모나드를 나타내는 제네릭 형식을 구현하고 필요한 세 가지 작업을 나타내는 메서드를 구현합니다. 값을 증폭 된 값으로 변환, (아마도) 증폭 된 값을 값으로 변환 및 함수를 증폭되지 않은 값으로 변환 증폭 된 값에 대한 함수.

시작하기 좋은 곳은 C #에서 LINQ를 구현 한 방법입니다. SelectMany방법을 연구하십시오 . 시퀀스 모나드가 C #에서 어떻게 작동하는지 이해하는 것이 중요합니다. 매우 간단한 방법이지만 매우 강력합니다!


제안, 추가 읽기 :

  1. C #에서 모나드에 대한보다 심층적이고 이론적 인 설명 을 위해 주제에 대한 저의 ( Eric Lippert ) 동료 Wes Dyer의 기사를 강력히 추천 합니다. 이 기사는 모나드가 마지막으로 “클릭”했을 때 설명했습니다.
  2. 왜 모나드를 원할 수 있는지에 대한 좋은 예입니다 (예제에서 Haskell 사용) .
  3. 이전 기사의 “번역”을 JavaScript로 분류합니다.


답변

왜 모나드가 필요합니까?

  1. 함수 만 사용하여 프로그램하고 싶습니다 . (결국 -FP 후에 “기능 프로그래밍”).
  2. 그런 다음 첫 번째 큰 문제가 있습니다. 이것은 프로그램입니다 :

    f(x) = 2 * x

    g(x,y) = x / y

    무엇을 먼저 실행해야하는지 어떻게 알 수 있습니까? 우리는 어떻게 함수를 사용하여 정렬 된 순서의 함수 (즉 , 프로그램 )를 형성 할 수 있습니까?

    솔루션 : 작성 기능 . 당신이 먼저 원하는 g다음 f, 그냥 작성하십시오 f(g(x,y)). 좋습니다만 …

  3. 더 많은 문제 : 일부 기능 이 실패 할 수 있습니다 (예 : g(2,0)0으로 나누기). FP에는 “예외”없습니다 . 우리는 그것을 어떻게 해결합니까?

    솔루션 : 함수가 두 종류의 것을 반환하도록 하겠습니다 : g : Real,Real -> Real(두 개의 실수에서 실제로 기능하는) 대신 , g : Real,Real -> Real | Nothing(두 개의 실수에서 (실제로 또는 아무것도)로) 함수를 허용합시다 .

  4. 그러나 함수는 (단순하게) 한 가지만 반환 해야 합니다.

    솔루션 : 반환 할 새로운 유형의 데이터, 즉 실제 또는 전혀 아무것도 포함하지 않는 ” boxing type “을 만들어 봅시다 . 따라서 우리는 가질 수 있습니다 g : Real,Real -> Maybe Real. 좋습니다만 …

  5. 이제 어떻게됩니까 f(g(x,y))? f을 (를) 사용할 준비가되지 않았습니다 Maybe Real. 그리고 우리 g는를 사용할 수있는 모든 함수를 변경하고 싶지 않습니다 Maybe Real.

    솔루션 : “connect”/ “compose”/ “link”기능을위한 특수 기능을 갖도록 합시다 . 이런 식으로, 우리는 뒤에서 한 함수의 출력을 다음 함수를 제공하도록 조정할 수 있습니다.

    우리의 경우 : g >>= f(connect / compose gto f). 우리는 출력 >>=을 얻고 g, 검사하고, Nothing단지 호출 f하고 반환 하지 않는 경우를 원합니다 Nothing. 또는 반대로, 박스를 추출하여 Real공급 f하십시오. (이 알고리즘은 유형 에 >>=대한 구현 일뿐입니다 Maybe).

  6. 동일한 패턴을 사용하여 해결할 수있는 다른 많은 문제가 발생합니다. 1. “상자”를 사용하여 다른 의미 / 값을 코드화 / 저장하고 g이러한 “상자 값”을 반환하는 것과 같은 기능을 갖습니다 . 2. 의 출력을 의 입력에 g >>= f연결하는 데 도움을주는 작곡가 / 링커 가 있으므로 전혀 변경할 필요가 없습니다 .gff

  7. 이 기술을 사용하여 해결할 수있는 놀라운 문제는 다음과 같습니다.

    • 일련의 기능 ( “프로그램”)에서 모든 기능이 공유 할 수있는 전역 상태를 갖는 경우 : solution StateMonad.

    • 우리는 “불완전한 기능”을 좋아하지 않습니다 : 동일한 입력에 대해 다른 출력을 생성하는 기능 . 따라서 해당 함수를 표시하여 태그 / 박스 값을 반환합니다 : monad.IO

총 행복 !!!!


답변

모나드와 가장 유사한 OO 비유는 ” 명령 패턴 “이라고합니다.

명령 패턴에서는 일반 명령문 또는 표현식을 명령 오브젝트로 랩핑 합니다. 명령 객체 는 래핑 된 명령문을 실행 하는 실행 메소드를 노출합니다 . 따라서 문은 일급 객체로 바뀌어 마음대로 전달하고 실행할 수 있습니다. 명령 개체를 연결하고 중첩하여 프로그램 개체를 만들 수 있도록 명령을 구성 할 수 있습니다.

명령은 별도의 객체 invoker에 의해 실행됩니다 . 일련의 일반 문을 실행하는 대신 명령 패턴을 사용하면 다른 호출자가 명령 실행 방법에 다른 논리를 적용 할 수 있다는 이점이 있습니다.

명령 패턴을 사용하여 호스트 언어에서 지원하지 않는 언어 기능을 추가하거나 제거 할 수 있습니다. 예를 들어, 예외가없는 가상의 OO 언어에서 “try”및 “throw”메소드를 명령에 노출하여 예외 의미를 추가 할 수 있습니다. 명령 호출이 throw되면 호출자는 마지막 “시도”호출까지 명령 목록 (또는 트리)을 역 추적합니다. 반대로, 각 개별 명령에 의해 발생 된 모든 예외를 포착 한 다음 오류 코드로 변환하여 다음 명령으로 전달 함으로써 언어에서 예외 시맨틱을 제거 할 수 있습니다 ( 예외가 나쁜 것으로 판단되는 경우 ).

트랜잭션, 비 결정적 실행 또는 연속과 같은 훨씬 더 멋진 실행 의미는 기본적으로 지원하지 않는 언어로 이와 같이 구현 될 수 있습니다. 당신이 그것에 대해 생각하면 그것은 매우 강력한 패턴입니다.

이제 실제로는 명령 패턴이 이와 같은 일반적인 언어 기능으로 사용되지 않습니다. 각 문장을 별도의 클래스로 바꾸는 오버 헤드로 인해 참을 수없는 상용구 코드가 생길 수 있습니다. 그러나 원칙적으로 fp에서 모나드를 사용하는 것과 동일한 문제를 해결하는 데 사용할 수 있습니다.


답변

OOP 프로그래머가 (기능적 프로그래밍 배경없이) 이해한다는 관점에서 모나드는 무엇입니까?

어떤 문제가 해결되고 가장 많이 사용되는 장소는 무엇입니까? 가장 많이 사용되는 장소는 무엇입니까?

OO 프로그래밍의 관점에서 모나드는 두 가지 방법으로 유형별로 매개 변수가 지정된 인터페이스 (또는 믹스 인 일 가능성 있음) return이며 다음 bind을 설명합니다.

  • 주입 된 값 유형의 모노 값을 얻기 위해 값을 주입하는 방법;
  • 비 모나 딕 값에서 모나 딕 값을 모나 딕 값으로 만드는 함수를 사용하는 방법.

이 문제가 해결하는 문제는 인터페이스에서 기대할 수있는 것과 같은 유형의 문제입니다. 즉, “다른 일을하는 여러 가지 클래스가 있지만 그와 비슷한 방식으로 다른 일을하는 것 같습니다.” 클래스 자체가 실제로 ‘Object’클래스 자체보다 가까운 하위 유형이 아닌 경우에도 이들 간의 유사성을 설명 할 수 있습니까? “

보다 구체적으로, Monad“인터페이스”는 그 자체가 타입을 취하는 타입을 취한다는 점 과 유사 IEnumerator하거나 유사하다 IIterator. Monad그럼에도 불구하고 주요 “포인트” 는 메인 클래스의 정보 구조를 유지하거나 강화하면서 새로운 “내부 유형”을 갖는 점까지도 내부 유형을 기반으로 작업을 연결할 수 있습니다.


답변

크리스토퍼 리그 (2010 년 7 월 12 일)의 ” Monadologie-유형 불안에 대한 전문적인 도움 “이 발표 되었습니다 .
이 (슬라이드 쉐어) 프레젠테이션과 함께 진행되는 비디오는 실제로 vimeo에서 이용할 수 있습니다 .
Monad 파트는이 1 시간짜리 비디오에서 약 37 분 안에 시작하여 58 개의 슬라이드 프리젠 테이션 중 슬라이드 42로 시작합니다.

“함수 프로그래밍을위한 최고의 디자인 패턴”으로 표시되지만이 예제에 사용 된 언어는 OOP와 기능성 인 Scala입니다.
당신은 블로그 게시물에서 스칼라에서 모나드에 더 읽을 수있다 ” 모나드 – 스칼라 추상 계산하는 또 다른 방법 “에서, Debasish 고쉬 (3 월 (27), 2008).

형식 생성자 M은 다음 작업을 지원하는 경우 모나드입니다.

# the return function
def unit[A] (x: A): M[A]

# called "bind" in Haskell
def flatMap[A,B] (m: M[A]) (f: A => M[B]): M[B]

# Other two can be written in term of the first two:

def map[A,B] (m: M[A]) (f: A => B): M[B] =
  flatMap(m){ x => unit(f(x)) }

def andThen[A,B] (ma: M[A]) (mb: M[B]): M[B] =
  flatMap(ma){ x => mb }

예를 들어 (스칼라에서) :

  • Option 모나드입니다
    데프 유닛 [A] (x : A) : 옵션 [A] = 약간 (x)

    def flatMap [A, B] (m : 옵션 [A]) (f : A => 옵션 [B]) : 옵션 [B] =
      m 일치 {
       case None => 없음
       case Some (x) => f (x)
      }
  • List 모나드입니다
    데프 유닛 [A] (x : A) :리스트 [A] =리스트 (x)

    def flatMap [A, B] (m : 목록 [A]) (f : A => 목록 [B]) : 목록 [B] =
      m 일치 {
        경우 Nil => Nil
        사례 x :: xs => f (x) ::: flatMap (xs) (f)
      }

Monad는 Monad 구조를 이용하기 위해 만들어진 편리한 구문 때문에 스칼라에서 큰 문제입니다.

for스칼라에 대한 이해 :

for {
  i <- 1 to 4
  j <- 1 to i
  k <- 1 to j
} yield i*j*k

컴파일러는 다음과 같이 번역합니다.

(1 to 4).flatMap { i =>
  (1 to i).flatMap { j =>
    (1 to j).map { k =>
      i*j*k }}}

주요 추상화는 flatMap체인을 통해 계산을 바인딩하는입니다.
각 호출은 flatMap체인의 다음 명령에 대한 입력으로 사용되는 동일한 데이터 구조 유형 (그러나 다른 값) 을 리턴합니다.

위의 스 니펫에서 flatMap은 클로저를 입력으로 사용 (SomeType) => List[AnotherType]하고를 반환합니다 List[AnotherType]. 주목해야 할 중요한 점은 모든 flatMap은 입력과 동일한 클로저 유형을 사용하고 출력과 동일한 유형을 반환한다는 것입니다.

이것이 계산 스레드를 “바인딩”하는 것입니다. 이해를위한 시퀀스의 모든 항목은 동일한 유형 제약 조건을 준수해야합니다.


두 가지 작업을 수행하면 (실패 할 수 있음) 다음과 같이 세 번째 작업에 결과를 전달합니다.

lookupVenue: String => Option[Venue]
getLoggedInUser: SessionID => Option[User]
reserveTable: (Venue, User) => Option[ConfNo]

그러나 Monad를 사용하지 않으면 다음과 같은 복잡한 OOP 코드를 얻을 수 있습니다.

val user = getLoggedInUser(session)
val confirm =
  if(!user.isDefined) None
  else lookupVenue(name) match {
    case None => None
    case Some(venue) =>
      val confno = reserveTable(venue, user.get)
      if(confno.isDefined)
        mailTo(confno.get, user.get)
      confno
  }

반면 Monad를 사용하면 모든 연산 작업과 같은 실제 유형 ( Venue, User)으로 작업하고 for 구문의 플랫 맵으로 인해 옵션 확인 항목을 숨길 수 있습니다.

val confirm = for {
  venue <- lookupVenue(name)
  user <- getLoggedInUser(session)
  confno <- reserveTable(venue, user)
} yield {
  mailTo(confno, user)
  confno
}

수익률 부분은 세 함수가 모두있는 경우에만 실행됩니다 Some[X]. 어떤 None직접 반환 될 것이다 confirm.


그래서:

Monads는 Functional Programing 내에서 정렬 된 계산을 허용합니다.이를 통해 DSL과 같은 멋진 구조화 된 형태로 동작 순서를 모델링 할 수 있습니다.

그리고 가장 큰 힘은 다양한 목적으로 사용되는 모나드를 응용 프로그램 내에서 확장 가능한 추상화로 구성 할 수 있다는 것입니다.

이 모나드에 의한 동작의 시퀀싱 및 스레딩은 폐쇄의 마법을 통해 변환을 수행하는 언어 컴파일러에 의해 수행됩니다.


그런데 Monad는 FP에서 사용되는 계산 모델이 아닙니다.

카테고리 이론은 많은 계산 모델을 제안합니다. 그들 중

  • 계산의 화살표 모델
  • 모나드 계산 모델
  • 적용 계산 모델

답변

빠른 독자를 존중하기 위해 먼저 정확한 정의부터 시작하여보다 “일반적인 영어”설명을 계속 한 다음 예제로 이동하십시오.

간결하고 정확한 정의는 다음과 같습니다 .

모나드 (컴퓨터 과학)는지도가 공식적이다 :

  • X주어진 프로그래밍 언어의 모든 유형 을 새로운 유형 T(X)( ” T에 값이있는 -computations “이라고 함)으로 보냅니다 X.

  • 두 가지 형태의 기능 구성에 대한 규칙 구비
    f:X->T(Y)하고 g:Y->T(Z)기능을 행 g∘f:X->T(Z);

  • 명백한 의미로 연관되고이라는 주어진 단위 함수와 관련하여, pure_X:X->T(X)그 값을 단순히 반환하는 순수한 계산에 값을 취하는 것으로 생각됩니다.

간단히 말해서 모나드모든 유형 X에서 다른 유형 T(X)으로 전달되는 규칙이며 두 함수 f:X->T(Y)에서 g:Y->T(Z)(구성하고 싶지만 작성할 수는 없음) 새로운 함수 로 전달h:X->T(Z) 하는 규칙 입니다. 그러나 엄격한 수학적 의미 의 구성은 아닙니다 . 우리는 기본적으로 함수의 구성을 “굽힘”하거나 함수 구성 방법을 재정의하고 있습니다.

또한, “명백한”수학 공리를 만족시키기 위해 모나드의 작성 규칙이 필요합니다.

  • 연관성 : 작곡 f으로 g다음으로는 h(외부) 구성과 동일해야 g함께 h다음으로 f(안쪽으로).
  • 단위 속성 : 양쪽 f항등 함수로 구성 하면 양보해야합니다 f.

다시 말하지만, 간단히 말해서, 우리가 원하는대로 함수 구성을 재정의하는 것에 열중 할 수는 없습니다.

  • 예를 들어 여러 함수를 한 행에 구성 할 수 있고 함수 f(g(h(k(x)))구성 순서를 지정하는 것에 대해 걱정하지 않으려면 먼저 연관성이 필요합니다 . 모나드 규칙은 그 공리없이 한 쌍의 함수 를 구성하는 방법 만 규정 하기 때문에 어떤 쌍이 먼저 구성되는지 등을 알아야합니다. ( f로 구성된 commutativity 특성과 다른 점은 g으로 g구성된 것과 같으므로 f필수는 아닙니다.)
  • 둘째, 우리는 단위 속성이 필요합니다. 이는 단순히 정체성이 우리가 기대하는 방식으로 사소하게 구성된다고 말하는 것입니다. 따라서 해당 ID를 추출 할 수있을 때마다 함수를 안전하게 리팩토링 할 수 있습니다.

간단히 말해서, 모나드는 타입 확장의 규칙이며 연관성과 단위 속성이라는 두 가지 공리를 만족시키는 함수를 구성합니다.

실제로는 함수 구성을 담당하는 언어, 컴파일러 또는 프레임 워크로 모나드를 구현하기를 원합니다. 따라서 함수가 어떻게 구현되는지 걱정하지 않고 함수의 논리를 작성하는 데 집중할 수 있습니다.

그것은 본질적으로 간단합니다.


전문 수학자이기 때문에 와 h의 “구성” 이라고 부르지 않는 것이 좋습니다. 수학적으로는 그렇지 않기 때문입니다. 그것을 “조성”이라고 부르는 것은 그것이 실제 수학적 구성 이라고 잘못 추정합니다 . 심지어 고유에 의해 결정되지 않습니다 및 . 대신 모나드의 새로운 “구성 규칙”의 결과입니다. 후자가 존재하더라도 실제 수학적 구성과는 완전히 다를 수 있습니다!fghfg


덜 건조하게 만들기 위해 작은 섹션으로 주석을 달고 있다는 것을 예로 들어 설명해 보겠습니다. 그래서 바로 건너 뛸 수 있습니다.

Monad 예제로 예외 발생

두 가지 기능을 작성한다고 가정 해보십시오.

f: x -> 1 / x
g: y -> 2 * y

그러나 f(0)정의되지 않았으므로 예외 e가 발생합니다. 그러면 작곡가를 어떻게 정의 할 수 g(f(0))있습니까? 물론 예외를 다시 던져라! 아마 같은 것 e입니다. 새로운 업데이트 된 예외 일 수 e1있습니다.

여기서 정확히 무슨 일이? 먼저 새로운 예외 값이 필요합니다 (다른 또는 동일). 당신이 그들을 호출 할 수 있습니다 nothing또는 null또는 무엇이든을하지만 본질은 동일하게 유지 – 그들은 그것이 안, 예를 들어, 새로운 값을해야 number우리의 예에서 여기. 특정 언어로 구현 null하는 방법과 혼동을 피하기 위해 전화하지 않는 것이 null좋습니다. 마찬가지로, 나는 원칙적으로 해야 할 일과 nothing관련이 있기 때문에 피하는 것을 선호 하지만, 그 원칙은 실제적인 이유에서 종종 구부러집니다.nullnull

예외 란 무엇입니까?

이것은 숙련 된 프로그래머에게는 사소한 문제이지만 혼란의 벌레를 없애기 위해 몇 마디 만 포기하고 싶습니다.

예외는 잘못된 실행 결과가 발생한 방법에 대한 정보를 캡슐화하는 개체입니다.

여기에는 세부 사항을 버리고 단일 전역 값 (예 : NaN또는 null)을 반환 하거나 긴 로그 목록을 생성하거나 정확히 무슨 일이 있었는지,이를 데이터베이스로 보내 분산 된 데이터 스토리지 계층 전체에 복제하는 것 등이 있습니다.

이 두 가지 예외 사례의 중요한 차이점은 첫 번째 경우 부작용없다는 것 입니다. 두 번째에는 있습니다. 이것은 우리에게 (천 달러) 질문을 가져옵니다.

순수한 기능에서 예외가 허용됩니까?

짧은 대답 : 예, 그러나 부작용을 일으키지 않을 때만 가능합니다.

더 긴 대답. 순수하게 함수의 출력은 입력에 의해 고유하게 결정되어야합니다. 따라서 예외라고 하는 새로운 추상 값 f으로 보내서 함수 를 수정합니다 . 입력 값에 의해 고유하게 결정되지 않은 외부 정보 가 값에 포함되지 않도록합니다 . 부작용이없는 예외의 예는 다음과 같습니다.0eex

e = {
  type: error,
  message: 'I got error trying to divide 1 by 0'
}

그리고 여기 부작용이 있습니다 :

e = {
  type: error,
  message: 'Our committee to decide what is 1/0 is currently away'
}

실제로 나중에 해당 메시지가 변경 될 수있는 경우에만 부작용이 있습니다. 그러나 절대로 변하지 않는다고 보장되면 그 가치는 유일하게 예측 가능 해져 부작용이 없습니다.

더 조용하게 만들기 위해. 돌아 오는 함수 42는 분명히 순수합니다. 그러나 누군가 미친 42값으로 변수를 변경 하기로 결정 하면 새로운 조건에서 동일한 기능이 순수하게 중단됩니다.

간단하게 본질을 설명하기 위해 객체 리터럴 표기법을 사용하고 있습니다. 불행히도 상황은 JavaScript와 같은 언어로 엉망입니다. 여기서 error함수 구성과 관련하여 우리가 원하는 방식으로 행동하는 유형은 아니지만 실제 유형은 이런 방식으로 행동 null하거나 NaN행동하지 않고 오히려 인공적이고 항상 직관적 인 것은 아닙니다. 유형 변환.

타입 확장

예외 내에서 메시지를 변경하고 싶을 E때 전체 예외 객체에 대해 새 유형 을 선언 한 다음 maybe number혼란스러운 이름을 제외하고는 유형 number또는 새 예외 유형 중 하나입니다. E그래서 정말 합집합 number | EnumberE. 특히, 그것은 우리가 어떻게 구성하고 싶은지에 달려 E있는데, 이것은 이름에 제안되거나 반영되지 않습니다 maybe number.

기능 구성이란 무엇입니까?

이것은 수학적 연산 촬영 기능이다
f: X -> Yg: Y -> Z함수로서의 조성물을 구성하고 h: X -> Z만족 h(x) = g(f(x)). 이 정의의 문제점은 결과 f(x)가의 인수로 허용되지 않는 경우에 발생합니다 g.

수학에서는 이러한 기능을 추가 작업없이 구성 할 수 없습니다. 우리 위 예 엄격 수학 용액 fg제거하는 0정의의 세트로부터 f. 새로운 정의 세트 (보다 제한적인 새 유형 x)를 사용 f하여와 (과 ) 조합 할 수 g있습니다.

그러나 프로그래밍에서 f이와 같은 정의를 제한하는 것은 그리 실용적이지 않습니다 . 대신 예외를 사용할 수 있습니다.

또는 다른 방법으로, 인공 값은 같은 생성 NaN, undefined, null, Infinity평가할 그래서 등 1/0으로 Infinity1/-0-Infinity. 그런 다음 예외를 발생시키지 않고 새로운 값을 표현식으로 되돌립니다. 예측 가능한 결과가 나오거나 그렇지 않을 수 있습니다.

1/0                // => Infinity
parseInt(Infinity) // => NaN
NaN < 0            // => false
false + 1          // => 1

그리고 우리는 계속 이동할 준비가 된 정규 숫자로 돌아갑니다.)

JavaScript를 사용하면 위의 예와 같이 오류를 발생시키지 않고 어떤 식 으로든 숫자 식을 계속 실행할 수 있습니다. 즉, 기능을 구성 할 수도 있습니다. 정확히 모나드에 관한 것입니다-이 답변의 시작 부분에 정의 된 공리를 만족시키는 함수를 작성하는 규칙입니다.

그러나 숫자 오류를 처리하기위한 JavaScript의 구현에서 발생하는 함수 구성 규칙은 모나드입니까?

이 질문에 답하기 위해 필요한 것은 공리를 확인하는 것입니다 (여기서는 질문의 일부가 아닌 운동으로 남습니다).

예외 처리를 사용하여 모나드를 구성 할 수 있습니까?

실제로,보다 유용한 모나드는 대신 f일부 예외를 던질 경우 x, 그 구성도 any로 구성되도록 규정하는 규칙이 될 것 g입니다. 또한 E하나의 가능한 값 ( 범주 이론의 터미널 객체) 으로 예외를 전역 적으로 고유 하게 만듭니다 . 이제 두 가지 공리를 즉시 확인할 수 있으며 매우 유용한 모나드를 얻습니다. 그리고 결과는 아마도 모나드 로 잘 알려진 입니다.


답변

모나드는 값을 캡슐화하고 본질적으로 두 가지 연산을 적용 할 수있는 데이터 유형입니다.

  • return x 캡슐화하는 모나드 타입의 값을 생성 x
  • m >>= f( “바인드 연산자”로 읽음) f모나드의 값에 함수 를 적용m

그것이 모나드입니다. 있다 몇 가지 더 교칙는 하지만, 기본적으로이 두 작업은 모나드를 정의합니다. 실제 질문은 “모나드의 기능 무엇입니까 ?”이며 모나드에 따라 다릅니다. 목록은 모나드, 아마도 모나드, IO 작업은 모나드입니다. 우리가 그 것들을 모나드라고 말할 때, 모나드 인터페이스는 return및로 구성 >>=됩니다.