모나드가 상속 계층에 대한 실행 가능한 대안일까요?


20

먼저 monoid를 설명하는 이와 같은 모나드에 대해 언어에 구애받지 않는 설명 을 사용하겠습니다 .

모노 이드는 파라미터로서 몇 가지 유형을 가지고 동일한 유형을 반환하는 함수의 (대략) 세트이다.

모나드 테이크 함수 (약) 세트이다 래퍼 인자로 타입과 동일한 타입 래퍼를 반환한다.

그것들은 정의가 아니라 설명입니다. 그 설명을 자유롭게 공격하십시오!

따라서 OO 언어에서 모나드는 다음과 같은 연산 구성을 허용합니다.

Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()

모나드는 포함 된 클래스가 아닌 해당 작업의 의미를 정의하고 제어합니다.

전통적으로 OO 언어에서는 클래스 계층 구조와 상속을 사용하여 이러한 의미를 제공합니다. 그래서 우리는 거라고 Bird방법과 클래스를 takeOff(), flyAround()그리고 land(), 오리는 사람들을 상속합니다.

그러나 우리는 날지 못하는 새들 때문에 어려움을 겪습니다 penguin.takeOff(). 실패 하기 때문 입니다. 예외 처리 및 처리에 의지해야합니다.

또한 펭귄이 a라고 말하면 Bird계층 구조가 여러 개인 경우와 같이 다중 상속에 문제가 발생 Swimmer합니다.

본질적으로 우리는 클래스를 범주에 넣고 (범주 이론 녀석에게 사과와 함께) 개별 클래스가 아닌 범주별로 의미를 정의하려고합니다. 그러나 모나드는 계층 구조보다 훨씬 명확한 메커니즘처럼 보입니다.

이 경우 Flier<T>위의 예와 같은 모나드 가 있습니다 .

Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()

... 우리는 결코 인스턴스화하지 않을 것 Flier<Penguin>입니다. 우리는 정적 타이핑을 사용하여 마커 인터페이스와 함께 발생하지 않도록 할 수도 있습니다. 또는 런타임 기능 검사로 구제 할 수 있습니다. 그러나 실제로 프로그래머는 절대로 펭귄을 Flier에 넣지 말아야합니다. 같은 의미에서 결코 0으로 나누지 않아야합니다.

또한 더 일반적으로 적용됩니다. 전단지는 새일 필요는 없습니다. 예를 들어 Flier<Pterodactyl>, 또는 Flier<Squirrel>개별 유형의 의미를 변경하지 않은 경우.

유형 계층 구조가 아닌 컨테이너의 구성 가능한 함수로 의미를 분류하면 특정 계층 구조에 맞는 "종류, 유형, 종류"가 아닌 클래스의 오래된 문제를 해결합니다. 또한 클래스 Flier<Duck>뿐만 아니라 여러 의미를 쉽고 명확하게 허용합니다 Swimmer<Duck>. 클래스 계층과 동작을 분류하여 임피던스 불일치로 어려움을 겪고있는 것 같습니다. 모나드는 그것을 우아하게 처리합니다.

그래서 제 질문은 상속보다 구성을 선호하는 것과 같은 방식으로 상속보다 모나드를 선호하는 것이 합리적입니까?

(BTW 이것이 여기에 있는지 아니면 Comp Sci에 있는지 확실하지 않지만 실제 모델링 문제처럼 보입니다. 그러나 아마도 더 좋을 것입니다.)


1
작동 방식을 잘 모르겠습니다. 다람쥐와 오리는 같은 방식으로 비행하지 않습니다. 따라서 해당 클래스에서 "비행 행동"을 구현해야합니다. 전단지에는 다람쥐와 오리를 만드는 방법이 필요합니다. 플라이 ... 아마도 일반적인 Flier 인터페이스에서 ... 잠시만 기다려주세요 ... 뭔가 빠졌습니까?
assylias

인터페이스는 기능을 정의하고 기능 상속은 실제 동작을 정의하므로 인터페이스는 클래스 상속과 다릅니다. "상속 구성"에서도 인터페이스 정의는 여전히 중요한 메커니즘입니다 (예 : 다형성). 인터페이스는 동일한 다중 상속 문제에 부딪치지 않습니다. 또한 각 전단지는 컨테이너가 사용할 "getFlightSpeed ​​()"또는 "getManuverability ()"와 같은 (인터페이스 및 다형성을 통해) 기능 속성을 제공 할 수 있습니다.
Rob

3
파라 메트릭 다형성을 사용하는 것이 항상 아형 다형성에 대한 실행 가능한 대안인지 묻고 자합니까?
ChaosPandion

의미를 보존하는 구성 가능한 기능을 추가하는 주름이 있습니다. 매개 변수가있는 컨테이너 유형은 오랫동안 사용되어 왔지만 그 자체로는 완전한 대답이라고 생각하지 않습니다. 그래서 모나드 패턴이 더 근본적인 역할을하는지 궁금합니다.
Rob

6
귀하의 모노 이드 및 모나드에 대한 귀하의 설명을 이해하지 못합니다. monoids의 주요 속성은 연관 이진 연산 (부동 소수점 추가, 정수 곱셈 또는 문자열 연결을 생각)을 포함한다는 것 입니다. 모나드 홀더가 추상화이다 시퀀싱 어떤 순서로 다양한 (아마도 의존적)를 계산.
Rufflewind

답변:


15

짧은 대답은 no입니다 . 모나드는 상속 계층 구조 (하위 유형 다형성이라고도 함)의 대안이 아닙니다. 모나드가 사용 하는 파라 메트릭 다형성을 설명하는 것 같지만 그렇게하는 것은 아닙니다.

내가 이해하는 한 모나드는 본질적으로 상속과 관련이 없습니다. 나는 두 가지가 거의 직교라고 말하고 싶습니다. 그것들은 다른 문제를 해결하기위한 것입니다.

  1. 적어도 두 가지 의미에서 시너지 효과를 발휘할 수 있습니다.
    • Haskell의 많은 유형 클래스를 다루는 Typeclassopedia를 확인하십시오 . 그들 사이에는 상속과 같은 관계가 있음을 알 수 있습니다. 예를 들어 Monad는 Applicative의 자손이며 Functor의 자손입니다.
    • Monads의 인스턴스 인 데이터 유형은 클래스 계층에 참여할 수 있습니다. Monad는 인터페이스와 비슷합니다. 주어진 유형에 대해 구현하면 데이터 유형에 대한 정보가 있지만 전부는 아닙니다.
  2. 하나를 사용하여 다른 하나를 시도하는 것은 어렵고 추악합니다.

마지막으로, 이것은 귀하의 질문에 대한 접선이지만 모나드에는 구성 할 수있는 매우 강력한 방법이 있다는 것을 배우고 싶을 것입니다. 자세한 내용은 모나드 변압기를 읽어보십시오. 그러나, 우리는 (나보다 100 만배 더 똑똑한 사람들이 모나드를 구성하는 훌륭한 방법을 찾지 못했고, 일부 모나드가 임의로 구성하지 않는 것처럼 보이기 때문에) 이것은 여전히 ​​활발한 연구 영역입니다.


이제 귀하의 질문을 nit-pick하기 위해 (죄송합니다. 이것이 도움이되고 나쁘게 느끼지 않기를 바랍니다)

  1. 모나드는 컨테이너 유형을 매개 변수로 사용하고 동일한 컨테이너 유형을 리턴하는 함수 세트입니다.

    아니, 이것은 이다 Monad하스켈 : 파라미터 화 된 형태 m a의 구현 return :: a -> m a(>>=) :: m a -> (a -> m b) -> m b다음과 같은 법칙을 만족 :

    return a >>= k  ==  k a
    m >>= return  ==  m
    m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h
    

    컨테이너 ( (->) b) 가 아닌 Monad의 일부 인스턴스 Set가 있으며 형식 클래스 제약으로 인해 Monad ()의 인스턴스가 아니거나 만들 수없는 컨테이너가 있습니다 ( ). "컨테이너"직관은 좋지 않습니다. 더 많은 예를 보려면 이것을 참조하십시오 .

  2. 따라서 OO 언어에서 모나드는 다음과 같은 연산 구성을 허용합니다.

      Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
    

    아뇨, 전혀 아닙니다. 이 예에는 Monad가 필요하지 않습니다. 입력 및 출력 유형이 일치하는 기능 만 있으면됩니다. 함수 응용 프로그램이라는 것을 강조하는 또 다른 방법은 다음과 같습니다.

    Flier<Duck> m = land(flyAround(takeOff(new Flier<Duck>(duck))));
    

    나는 이것이 "유창한 인터페이스"또는 "메소드 체인"이라고 알려진 패턴이라고 생각합니다 (그러나 확실하지는 않습니다).

  3. 모나드는 포함 된 클래스가 아닌 해당 작업의 의미를 정의하고 제어합니다.

    모나드이기도 한 데이터 유형은 모나드와 관련이없는 작업을 가질 수 있습니다. 다음 []은 모나드와 관련이없는 세 가지 함수로 구성된 Haskell 예제입니다 . []"작업의 의미를 정의하고 제어합니다"및 "포함 된 클래스"는 모나드를 만들기에는 충분하지 않습니다.

    \predicate -> length . filter predicate . reverse
    
  4. 클래스 계층 구조를 사용하여 사물을 모델링하는 데 문제가 있음을 올바르게 알고 있습니다. 그러나 귀하의 예는 모나드가 다음을 수행 할 수 있다는 증거를 제시하지 않습니다.

    • 상속이 잘되는 일을 잘해라
    • 상속이 나쁜 물건을 잘해라

3
고맙습니다! 처리 할 것이 많습니다. 기분 나쁘지 않습니다. 통찰력을 높이 평가합니다. 나는 나쁜 생각을 나르는 것이 더 나빠질 것입니다. :) (stackexchange의 전체 지점으로 이동합니다!)
Rob

1
@RobY 천만에요! 그건 그렇고, 전에 들어 본 적이 없다면 LYAH 를 추천합니다. 모나드 (및 Haskell!)를 배우는 훌륭한 소스이기 때문에 많은 예제가 있기 때문에 (그리고 많은 예제를하는 것이 가장 좋은 방법이라고 생각합니다 모나드 태클).

여기에는 많은 것이 있습니다. 나는 의견을 늪지 않고 싶지만 몇 가지 의견 : # 2 land(flyAround(takeOff(new Flier<Duck>(duck))))는 작동하지 않습니다 (적어도 OO에서는). 클래스에서 작업을 연결함으로써 Flier의 세부 정보는 숨겨져 있으며 그 의미를 보존 할 수 있습니다. 즉 하스켈에서 모나드 바인딩은 이유 비슷 (a, M b)하지 (M a, M b)그래서 모나드는 "작업"기능에 그 상태를 노출하지 않습니다.
Rob

# 1, 불행히도 Haskell에서 Monad의 엄격한 정의를 흐리게 하려고 합니다. 하스켈에 매핑하는 것은 큰 문제가 있습니다. 생성자의 컴포지션을 포함한 함수 구성 은 Java와 같은 보행자 언어로는 쉽게 할 수 없습니다. 따라서 unit포함 된 유형에서 (주로) 생성자 bind가되고 "조치"함수를 클래스에 연결 하는 암시 적 컴파일 시간 작업 (즉, 초기 바인딩)이됩니다. 퍼스트 클래스 함수 또는 Function <A, Monad <B >> 클래스를 가지고 있다면, bind메소드는 바인딩을 늦게 할 수 있지만 다음에 그 남용을 취하겠습니다. ;)
Rob

# 3 동의합니다. 그것이 그 아름다움입니다. Flier<Thing>비행의 의미를 제어하는 경우 비행 의미를 유지하는 많은 데이터와 작업을 노출 할 수 있지만 "모나드"별 의미는 실제로 체인화 및 캡슐화를 만드는 것입니다. 이러한 우려는 모나드 내부의 클래스에 대한 우려가 아닐 수도 있습니다 (예 : Resource<String>httpStatus 속성이 있지만 String은 그렇지 않습니다).
Rob

1

그래서 제 질문은 상속보다 구성을 선호하는 것과 같은 방식으로 상속보다 모나드를 선호하는 것이 합리적입니까?

비 OO 언어에서는 그렇습니다. 더 전통적인 OO 언어에서는 아니오라고 말할 것입니다.

문제는 대부분의 언어는 당신이 할 수없는 의미 유형의 전문화를 필요가 없다는 것입니다 Flier<Squirrel>Flier<Bird>다른 구현이있다. 당신은 static Flier Flier::Create(Squirrel)(그리고 각 유형마다 과부하) 같은 것을해야합니다 . 이는 새로운 동물을 추가 할 때마다이 유형을 수정해야하며,이를 위해 약간의 코드를 복제해야 할 수도 있습니다.

아, 그리고 몇몇 언어 (예 : C #) public class Flier<T> : T {}는 불법입니다. 심지어 빌드되지 않습니다. 모든 OO 프로그래머가 아니지만 대부분은 Flier<Bird>여전히 Bird.


의견 주셔서 감사합니다. 더 많은 생각이 있지만 Flier<Bird>매개 변수가있는 컨테이너 일지라도 사소하게 Bird(!?) List<String>문자열이 아닌 List라고 생각하는 사람은 없습니다.
Rob

@RobY- 단순한 컨테이너 Flier가 아닙니다 . 컨테이너라고 생각하면 상속을 대체 할 수 있다고 생각하는 이유는 무엇입니까?
Telastyn

나는 당신을 잃었습니다 ... 내 요점은 모나드가 강화 된 컨테이너라는 것입니다. Animal / Bird / Penguin그것은 모든 종류의 의미를 가져 오기 때문에 일반적으로 나쁜 예입니다. 실제적인 예는 우리가 사용하고 지내는 틱 모나드입니다 : Resource<String>.from(uri).get() Resource상단에 의미를 추가 String는 분명하지, 그래서 (또는 다른 타입) String.
Rob

@RobY-그러나 상속과 관련이 없습니다.
Telastyn

다른 종류의 격리라는 것을 제외하고. String을 Resource에 넣거나 ResourceString 클래스를 추상화하고 상속을 사용할 수 있습니다. 내 생각은 클래스를 체인 컨테이너에 넣는 것이 상속으로 클래스 계층 구조에 넣는 것보다 행동을 추상화하는 더 좋은 방법이라고 생각합니다. 따라서 "대체 / 반복"이라는 의미에서 "관련된 방법은 없습니다"-예.
Rob
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.