관용적이거나 열악한 디자인과 패턴 일치가 있습니까?


18

F # 코드는 종종 유형과 패턴 일치하는 것처럼 보입니다. 확실히

match opt with 
| Some val -> Something(val) 
| None -> Different()

일반적인 것 같습니다.

그러나 OOP 관점에서 볼 때 런타임 유형 검사를 기반으로하는 제어 흐름과 끔찍한 것처럼 보이며 일반적으로 눈살을 찌푸 리게됩니다. 이를 설명하기 위해 OOP에서 오버로드를 사용하는 것이 좋습니다.

type T = 
    abstract member Route : unit -> unit

type Foo() = 
    interface T with
        member this.Route() = printfn "Go left"

type Bar() = 
    interface T with
        member this.Route() = printfn "Go right"

이것은 분명히 더 많은 코드입니다. OTOH, 내 OOP-y 생각에 구조적 이점이있는 것 같습니다.

  • 새로운 형태로의 확장 T은 쉽다;
  • 경로 선택 제어 흐름의 중복을 찾는 것에 대해 걱정할 필요가 없습니다. 과
  • 경로 선택은 손에 든 후에는 구현에 Foo대해 걱정할 필요가 없다는 점에서 불변입니다.Bar.Route()

보이지 않는 유형에 대한 패턴 일치의 이점이 있습니까? 관용어로 간주됩니까 아니면 일반적으로 사용되지 않는 기능입니까?


4
OOP 관점에서 기능적 언어를 보는 것이 얼마나 의미가 있습니까? 어쨌든 패턴 매칭의 진정한 힘은 중첩 된 패턴과 함께 제공됩니다. 가장 바깥 쪽 생성자를 확인하는 것이 가능하지만 결코 전체 이야기는 아닙니다.
Ingo

이것은 But from an OOP perspective, that looks an awful lot like control-flow based on a runtime type check, which would typically be frowned on.너무 독단적으로 들립니다. 때때로, 당신은 당신의 계층 구조에서 당신의 op를 분리하기를 원할 것입니다 : 아마도 1) 당신은 당신이 계층 구조를 소유하지 않은 b / c 계층 구조에 op를 추가 할 수 없습니다; 2) 작전을 원하는 수업이 계층과 일치하지 않습니다. 3) 계층 구조에 op를 추가 할 수는 있지만 대부분의 클라이언트가 사용하지 않는 많은 쓰레기로 계층 구조의 API를 혼란스럽게하고 싶지는 않습니다.

4
그냥 명확히 할 수 SomeNone유형이 아니다. 둘 다 형식이 forall a. a -> option a있고 forall a. option a(죄송합니다. 형식 주석의 구문이 F #인지 확실하지 않은) 생성자입니다 .

답변:


21

OOP 클래스 계층 구조는 F #의 차별 조합과 밀접하게 관련되어 있으며 패턴 일치는 동적 유형 테스트와 매우 밀접하게 관련되어 있습니다. 실제로, 이것은 실제로 F #이 구별 된 공용체를 .NET으로 컴파일하는 방법입니다!

확장 성과 관련하여 문제의 두 가지 측면이 있습니다.

  • OO를 사용하면 새로운 하위 클래스를 추가 할 수 있지만 새로운 (가상) 기능을 추가하기 어렵습니다
  • FP를 사용하면 새로운 기능을 추가 할 수 있지만 새로운 통합 사례를 추가하기 어렵습니다.

즉, 패턴 일치에서 사례를 놓칠 때 F #에서 경고를 표시하므로 새 조합 사례를 추가하는 것이 실제로 그렇게 나쁘지는 않습니다.

루트 선택에서 중복을 찾는 것과 관련하여-F #은 일치하는 중복 항목이있을 때 경고를 표시합니다. 예 :

match x with
| Some foo -> printfn "first"
| Some foo -> printfn "second" // Warning on this line as it cannot be matched
| None -> printfn "third"

"경로 선택은 불변이다"는 사실도 문제가 될 수 있습니다. 예를 들어, 케이스 Foo와 함수간에 함수 구현을 공유하고 Bar싶지만 케이스에 대해 다른 작업을 수행하려는 Zoo경우 패턴 일치를 사용하여 쉽게 인코딩 할 수 있습니다.

match x with
| Foo y | Bar y -> y * 20
| Zoo y -> y * 30

일반적으로 FP는 먼저 유형을 디자인 한 다음 기능을 추가하는 데 더 중점을 둡니다. 따라서 단일 파일의 두 줄로 유형 (도메인 모델)을 맞추고 도메인 모델에서 작동하는 기능을 쉽게 추가 할 수 있다는 점에서 실제로 도움이됩니다.

OO와 FP의 두 가지 접근 방식은 매우 보완 적이며 장점과 단점이 있습니다. OO 관점에서 오는 까다로운 일은 F #이 일반적으로 FP 스타일을 기본값으로 사용한다는 것입니다. 그러나 실제로 새로운 하위 클래스를 추가해야하는 경우 인터페이스를 항상 사용할 수 있습니다. 그러나 대부분의 시스템에서 유형과 함수를 동일하게 추가해야하므로 선택은 그다지 중요하지 않으며 F #에서 차별적 인 조합을 사용하는 것이 좋습니다.

자세한 내용은 이 훌륭한 블로그 시리즈 를 추천 합니다 .


3
당신이 맞지만, 이것은 객체 대 합계 유형의 문제이기 때문에 이것이 OO 대 FP의 문제가 아니라고 덧붙이고 싶습니다. OOP는 그들에 대한 집념을 제쳐두고, 기능하지 못하게 만드는 객체에 대해서는 아무것도 없습니다. 그리고 충분한 후프를 뛰어 넘으면 주류 OOP 언어로 합계 유형을 구현할 수도 있습니다 (예쁘지는 않지만).
Doval

1
"충분한 후프를 뛰어 넘으면 주류 OOP 언어로 합계 유형을 구현할 수도 있습니다 (예쁘지는 않지만)." -> .NET 유형 시스템에서 F # 합계 유형이 인코딩되는 방식과 비슷한 결과가
나올 것입니다.

8

패턴 일치 (본질적으로 과급 된 스위치 명령문)와 동적 디스패치가 유사하다는 것을 올바르게 관찰했습니다. 또한 일부 언어로 공존하며 매우 즐거운 결과를 얻습니다. 그러나 약간의 차이가 있습니다.

유형 시스템을 사용하여 고정 된 수의 하위 유형 만 가질 수있는 유형을 정의 할 수 있습니다.

// pseudocode
data Bool = False | True
data Option a = None | Some item:a
data Tree a = Leaf item:a | Node (left:Tree a) (right:Tree a)

이 것 결코 다른 하위 수 없습니다 Bool또는 Option, 서브 클래스는 스칼라와 같은 일부 언어이 처리 할 수를 서브 클래스의 개념이 (유용 표시되지 않도록 - 클래스가 현재 컴파일 단위의 "최종"외부로 표시 될 수 있지만, 서브 타입 이 컴파일 단위 내에서 정의 할 수 있습니다).

같은 유형의 하위 유형 Option은 이제 정적으로 알려져 있으므로 패턴 일치에서 대 / 소문자를 처리하지 않으면 컴파일러에서 경고 할 수 있습니다. 즉, 패턴 일치는 모든 옵션을 처리하도록하는 특수 다운 캐스트와 유사합니다.

또한 동적 메소드 디스패치 (OOP에 필요)도 런타임 유형 검사를 의미하지만 다른 종류입니다. 따라서이 유형 검사를 패턴 일치를 통해 명시 적으로 또는 메서드 호출을 통해 암시 적으로 수행하는 것은 상당히 관련이 없습니다.


"이것은 패턴 일치가 모든 옵션을 처리하도록 강요하는 특수 다운 캐스트와 유사하다는 것을 의미합니다."사실 (값이나 중첩 구조가 아닌 생성자와 만 일치하는 한) 동형이라고 생각합니다. 수퍼 클래스에 추상 가상 메소드를 배치합니다.
Jules

2

F # 패턴 일치는 일반적으로 클래스가 아닌 차별 된 공용체로 수행되므로 기술적으로 유형 검사가 아닙니다. 이를 통해 패턴 일치 사례를 고려하지 않은 경우 컴파일러에서 경고를 표시 할 수 있습니다.

주목해야 할 또 다른 사항은 기능적 스타일에서는 데이터가 아닌 기능별로 사물을 구성하므로 패턴 일치를 통해 여러 기능을 클래스에 흩어져 있지 않고 한 곳으로 모을 수 있습니다. 또한 변경이 필요한 곳 ​​옆에서 다른 사례가 어떻게 처리되는지 확인할 수 있다는 이점도 있습니다.

새 옵션을 추가하면 다음과 같습니다.

  1. 차별적 인 노조에 새로운 옵션 추가
  2. 불완전한 패턴 일치에 대한 모든 경고 수정

2

부분적으로, 유형을 사용하여 의사 결정을 더 자주 내리기 때문에 함수형 프로그래밍에서 더 자주 볼 수 있습니다. 나는 당신이 아마 무작위로 예제를 선택했다는 것을 알고 있지만, 패턴 일치 예제와 동등한 OOP는 더 자주 보일 것입니다 :

if (opt != null)
    opt.Something()
else
    Different()

다시 말해, OOP에서 널 검사와 같은 일상적인 일을 피하기 위해 다형성을 사용하는 것은 비교적 드 rare니다. OO 프로그래머가 모든 작은 상황에서 null 객체 를 생성하지 않는 것처럼 , 함수형 프로그래머는 특히 패턴 목록이 철저하다는 것을 알고있을 때 항상 함수에 과부하를주지 않습니다. 더 많은 상황에서 유형 시스템을 사용하면 익숙하지 않은 방식으로 사용됩니다.

반대로, OOP 예제와 동등한 관용적 기능 프로그래밍은 패턴 일치를 사용하지 않을 가능성이 높지만 호출 코드에 인수로 전달되는 함수 fooRoutebarRoute함수를 갖습니다 . 누군가가 그 상황에서 패턴 일치를 사용한 경우, 누군가 유형을 켜는 것이 OOP에서 잘못된 것으로 간주되는 것처럼 일반적으로 잘못된 것으로 간주됩니다.

그렇다면 패턴 매칭은 언제 좋은 기능적 프로그래밍 코드로 간주됩니까? 유형을 보는 것 이상을 수행하고 요구 사항을 확장 할 때 사례를 더 추가 할 필요가 없습니다. 예를 들어 type이있는 Some val것을 확인하는 것뿐만 아니라 다른 쪽의 type-safe 사용을 위해 기본 유형에 바인딩 합니다 . 세 번째 사례가 필요 없을 가능성이 높으므로 유용합니다. optSomeval->

패턴 일치는 표면적으로 객체 지향 스위치 문과 유사 할 수 있지만 특히 길거나 중첩 된 패턴의 경우 훨씬 더 많이 진행됩니다. 잘못 디자인 된 OOP 코드와 동등한 것으로 선언하기 전에 모든 것을 고려하십시오. 상속 계층 구조에서 명확하게 표현할 수없는 상황을 간결하게 처리하는 경우가 종종 있습니다.


난 당신이 알고 알고 답을 쓰는 동안 아마 당신의 마음을 미끄러하지만 참고 Some하고 None당신이 유형에 대한 패턴 매칭되지 않도록, 유형하지 않습니다. 동일한 유형의 생성자 에서 패턴 일치를 수행 합니다 . 이것은 "instanceof"를 묻는 것과는 다릅니다.
Andres F.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.