문제

내가 틀렸다면 나를 수정하지만 Haskell의 대수 데이터 유형은 OO 언어로 클래스와 상속을 사용할 수있는 많은 경우에 유용합니다. 그러나 큰 차이가 있습니다. 일단 대수 데이터 유형이 선언되면 다른 곳에서는 확장 할 수 없습니다. "폐쇄"입니다. OO에서는 이미 정의 된 클래스를 확장 할 수 있습니다. 예를 들어:

data Maybe a = Nothing | Just a

이 선언을 수정하지 않고 나중에이 유형에 다른 옵션을 추가 할 수있는 방법은 없습니다. 그렇다면이 시스템의 이점은 무엇입니까? OO 방식이 훨씬 더 확장 가능한 것 같습니다.

도움이 되었습니까?

해결책

The fact that ADT are closed makes it a lot easier to write total functions. That are functions that always produce a result, for all possible values of its type, eg.

maybeToList :: Maybe a -> [a]
maybeToList Nothing  = []
maybeToList (Just x) = [x]

If Maybe were open, someone could add a extra constructor and the maybeToList function would suddenly break.

In OO this isn't an issue, when you're using inheritance to extend a type, because when you call a function for which there is no specific overload, it can just use the implementation for a superclass. I.e., you can call printPerson(Person p) just fine with a Student object if Student is a subclass of Person.

In Haskell, you would usually use encapsulation and type classes when you need to extent your types. For example:

class Eq a where
   (==) :: a -> a -> Bool

instance Eq Bool where
  False == False = True
  False == True  = False
  True  == False = False
  True  == True  = True

instance Eq a => Eq [a] where
  []     == []     = True
  (x:xs) == (y:ys) = x == y && xs == ys
  _      == _      = False

Now, the == function is completely open, you can add your own types by making it an instance of the Eq class.


Note that there has been work on the idea of extensible datatypes, but that is definitely not part of Haskell yet.

다른 팁

답은 코드를 쉽게 확장 할 수있는 방법, 클래스 사이의 긴장과 Phil Wadler가 "표현 문제"라고 불렀던 대수 데이터 유형과 관련이 있습니다.

  • 대수 데이터 유형으로

    • 그것은 매우입니다 값이 싼 새로운 것을 추가합니다 사물에 대한 운영: 새로운 기능 만 정의합니다. 그러한 것들에 대한 모든 오래된 기능은 계속 변경되지 않습니다.

    • 그것은 매우입니다 값비싼 새로운 것을 추가합니다 일종의 일: 기존 데이터 유형에 새 생성자를 추가해야합니다. 해당 유형을 사용하는 모든 기능을 편집하고 다시 컴파일하십시오.

  • 수업과 함께

    • 그것은 매우입니다 값이 싼 새로운 것을 추가합니다 일종의 일: 새로운 서브 클래스를 추가하고 필요에 따라 해당 클래스에서 모든 기존 작업에 대해 전문화 된 방법을 정의하십시오. 슈퍼 클래스와 다른 모든 서브 클래스는 계속 변경되지 않습니다.

    • 그것은 매우입니다 값비싼 새로운 것을 추가합니다 사물에 대한 운영: 당신은해야합니다 슈퍼 클래스에 새 메소드 선언을 추가하십시오 그리고 잠재적으로 모든 기존 서브 클래스에 메소드 정의를 추가하십시오. 실제로, 부담은 방법에 따라 다릅니다.

따라서 폐쇄 형 유형이 특정 종류의 프로그램 진화를 잘 지원하기 때문에 대수 데이터 유형이 닫힙니다. 예를 들어, 데이터 유형이 언어를 정의하는 경우 이전 언어를 무효화하거나 데이터를 변경하지 않고 새로운 컴파일러 패스를 추가하기가 쉽습니다.

"열린"데이터 유형을 가질 수는 있지만 신중하게 제어 된 상황에서는 유형 검사가 어려워집니다. Todd Millstein은 일부를했습니다 매우 아름다운 작품 개방형 대수 유형 및 확장 가능한 기능을 지원하는 언어 설계에서 모두 모듈 식 체커가 있습니다. 나는 그의 논문을 읽는 것이 큰 기쁨을 발견했다.

당신이 같은 함수를 작성하는 경우

maybeToList Nothing = []
maybeToList (Just x) = [x]

그런 다음 모든 케이스를 다루었 기 때문에 런타임 오류가 발생하지 않을 것임을 알고 있습니다. 이것은 아마도 유형이 확장 될 수있는 즉시 사실이 아닙니다. 확장 가능한 유형이 필요한 경우 (그리고 생각보다 희귀합니다) 표준 Haskell 솔루션은 유형 클래스를 사용하는 것입니다.

"개방형 데이터 유형 및 열린 기능"을 확인하십시오. http://lambda-the-ultimate.org/node/1453

객체 지향 언어에서는 새 클래스를 정의하여 데이터를 쉽게 확장 할 수 있지만 새로운 기능을 추가하기는 어렵습니다. 기능적 언어에서는 상황이 반전됩니다. 새로운 기능을 추가하면 문제가 없지만 문제가 발생하지 않지만 문제는 없습니다. 데이터 확장 (새 데이터 생성자 추가) 기존 코드 수정이 필요합니다.. 확장 성 양방향을 지원하는 문제는 다음과 같습니다. 표현 문제. 우리는 Haskell 언어의 표현 문제에 대한 가벼운 솔루션으로 개방형 데이터 유형과 열린 기능을 제시합니다. 아이디어는 개방형 데이터 유형의 생성자와 개방 기능 방정식이 프로그램 전체에 흩어져있는 것처럼 보일 수 있다는 것입니다. 특히, 그들은 다른 모듈에 존재할 수 있습니다. 의도 된 의미론은 다음과 같습니다. 프로그램은 마치 데이터 유형과 함수가 한 곳에 정의 된 것처럼 행동해야합니다. 함수 방정식 순서는 가장 적합한 패턴 일치에 의해 결정되며, 여기서 특정 패턴은 비특이적 패턴보다 우선합니다. 우리의 솔루션은 표현 문제, 일반 프로그래밍 및 예외에 적용 할 수 있음을 보여줍니다. 우리는 두 가지 구현을 스케치합니다. 의미론에서 파생 된 간단한 것과 별도의 편집을 허용하는 상호 재귀 모듈을 기반으로합니다.

첫째, Charlie의 답변에 대한 대응으로 기능적 프로그래밍에 본질이 아닙니다. Ocaml은 개념을 가지고 있습니다 개방형 노조 또는 다형성 변이체, 본질적으로 원하는 것을 수행합니다.

에 관해서 , 나는이 선택이 Haskell을 위해 만들어 졌다고 생각합니다.

  • 이것은 유형을 예측 가능하게 할 수 있습니다.
  • 자신의 유형을 쉽게 정의 할 수 있습니다.
  • 많은 Haskell 기능은 다형성이며 클래스를 사용하면 기능 매개 변수에 맞게 사용자 정의 유형을 확장 할 수 있습니다 (Java의 인터페이스 생각).

그래서 당신이 오히려 가고 싶다면 data Color r b g = Red r | Blue b | Green g 유형은 쉽게 만들 수 있으며 Monad 또는 Functor 또는 다른 기능이 필요한 것처럼 쉽게 행동하게 할 수 있습니다.

이 (인정 된) 질문에 대한 훌륭한 대답은 몇 센트를 던져야한다고 생각합니다.

이 선언을 수정하지 않고 나중에이 유형에 다른 옵션을 추가 할 수있는 방법은 없습니다. 그렇다면이 시스템의 이점은 무엇입니까? OO 방식이 훨씬 더 확장 가능한 것 같습니다.

이것에 대한 답은 열린 금액이 당신에게주는 연장 성이 ~ 아니다 항상 플러스, 그리고 그에 따라 oo 이것은 당신에게 약점입니다.

폐쇄 된 노조의 장점은 그들의 것입니다 철저성: 컴파일 타임에 모든 대안을 수정 한 경우 코드가 처리 할 수없는 예상치 못한 사례가 없을 것입니다. 이것은 예를 들어 언어의 추상 구문 트리와 같은 많은 문제 영역에서 귀중한 속성입니다. 컴파일러를 작성하는 경우 언어 표현이 사전 정의 된 닫힌 서브 케이스 세트에 속합니다. ~ 아니다 사람들이 컴파일러가 이해하지 못하는 런타임에 새로운 서브 케이스를 추가 할 수 있기를 원합니다!

실제로, 컴파일러 AST는 방문자 패턴에 대한 4 가지 동기 부여 예제의 고전적인 갱 중 하나이며, 이는 OOP의 폐쇄 합계와 철저한 패턴 일치에 대한 대응 물입니다. OO 프로그래머가 폐쇄 합계를 복구하기위한 패턴을 발명했다는 사실을 반영하는 것은 유익합니다.

마찬가지로, 절차 적 및 기능적 프로그래머는 합의 효과를 얻기 위해 패턴을 발명했습니다. 가장 간단한 것은 "함수 레코드"인코딩이며, 이는 OO 인터페이스에 해당합니다. 기능 기록은 효과적으로 a 디스패치 테이블. (C 프로그래머는이 기술을 오랫동안 사용해 왔습니다!) 비결은 주어진 유형의 수많은 기능이 매우 많다는 것입니다. 따라서 필드가 기능인 레코드 유형이 있다면 천문학적으로 크거나 무한한 대안 세트를 쉽게 지원할 수 있습니다. 또한 레코드는 런타임에 생성되고 런타임 조건에 따라 유연하게 수행 될 수 있으므로 대안은 다음과 같습니다. 늦게 바운드.

내가 만든 마지막 의견은 내 마음에 OO가 너무 많은 사람들이 확장 성이 동의어라고 믿게 만들었다는 것입니다. 늦은 바인딩 (예 : 런타임에 유형에 새로운 서브 케이스를 추가하는 기능), 이것이 일반적으로 사실이 아닐 때. 늦은 바인딩입니다 하나의 기술 확장 가능성. 또 다른 기술입니다 구성- 빌딩 블록의 고정 어휘와 함께 조립하기위한 규칙으로 복잡한 물체를 구축합니다. 어휘와 규칙은 이상적으로 작지만 매우 복잡한 물건을 구축 할 수있는 풍부한 상호 작용을 갖도록 설계되었습니다.

기능적 프로그래밍 및 특히 ML/Haskell은 정적으로 입력 한 풍미로 인해 늦은 바인딩에 대한 구성을 오랫동안 강조했습니다. 그러나 실제로 두 종류의 기술은 두 패러다임에 존재하며 훌륭한 프로그래머의 툴킷에 있어야합니다.

또한 프로그래밍 언어 자체가 기본적으로 구성의 예라는 점도 주목할 가치가 있습니다. 프로그래밍 언어에는 요소를 결합하여 가능한 모든 프로그램을 작성할 수있는 유한하고 희망적으로 간단한 구문이 있습니다. (이것은 실제로 위의 컴파일러/방문자 패턴 예로 돌아가서 동기를 부여합니다.)

데이터 유형 및 전신 클래스 대 객체 지향 클래스를 보는 또 다른 직관적 인 방법은 다음과 같습니다.

수업 foo OO 언어에서는 두 가지 구체적인 유형을 나타냅니다 foo 뿐만 아니라 모두의 클래스 foo-유형 : 직간접 적으로 파생 된 것 foo.

OO 언어로, 당신은 foo-"확장"할 수있는 유형 foo.

좋아, "오픈"이라는 의미에서 당신은 "파생 될 수 있고"루비와 스몰 토크의 의미에서 열려 있지 않다는 것을 의미합니다.

어쨌든 두 가지를 주목하십시오. 첫째, 대부분의 OO 언어에서 주로 상속 기반 인 언어에서 상속 능력을 제한하도록 클래스를 선언하는 방법이 있습니다. Java에는 "최종"이 있으며 C ++에는 해킹이 있습니다. 따라서 다른 OO 언어에서는 기본값으로 기본 옵션을 만들고 있습니다.

둘째, 당신 ~할 수 있다 여전히 닫힌 ADT를 사용하고 다른 방법이나 다른 구현을 추가하는 새로운 유형을 만듭니다. 그래서 당신은 그런 식으로 제한되지 않습니다. 다시 말하지만, 그들은 공식적으로 같은 힘을 가진 것 같습니다. 하나로 표현할 수있는 것은 다른 하나로 표현할 수 있습니다.

실제는 기능적 프로그래밍이 실제로 다른 패러다임 ( "패턴")이라는 것입니다. OO 언어와 같아야한다는 기대와 함께 들어가면 정기적으로 놀라게됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top