2017-04-24 5 views
10

형식을 정의하고 그 형식에서 작동하는 함수 (인스턴스 메서드와 반대되는)를 설명하는 기능적 접근 방식을 취하면 코드를 어떻게 구성해야합니까?F # : 형식 및 모듈 구성을위한 모범 사례

(1) :

module MyType = 
    type MyType (x : int) = 
     member val X = x with get 

    let myFunction myType y = myType.X + y 

(2) :

type MyType (x : int) = 
    member val X = x with get 

[<RequireQualifiedAccess>] 
module MyTypeOps = 
    let myFunction myType y = myType.X + y 

(3) :

type MyType = 
    { 
     x : int 
    } 

    static member MyFunction myType y = myType.x + y 

프로

나는 일반적으로 세 가지 방법 중 하나로 이동 of (1)은 모듈에서 함수가 정의된다는 것입니다. (1)의 단점은 유형이 모듈에도 정의되어 있기 때문에 인스턴스화시에 MyType.MyType 리던던시가 발생하고 open MyType을 리던던시를위한 해결 방법으로 허용하려는 경우 [<RequireQualifiedAccess>]을 사용할 수 없다는 것입니다.

(2)의 장점은 모듈에 정의 된 기능이며 module.type 중복은 없습니다. Con은 모듈이 유형과 이름이 같을 수 없다는 것입니다.

Pro of (3)은 메소드가 정적이며 module.type 중복이 없다는 것입니다. 단점은이 방법으로 일부 유형 (예 : 비 메소드 값 및 활성 패턴)을 정의 할 수 없다는 것입니다.

일반적으로 나는 (2)를 선호합니다. 비록 모듈을 모듈화하지 않아도 될만한 것보다 덜 설명적이고 직관적 인 것을 명명하는 것을 싫어합니다. (2) 또한 드물게 접근 방식 (1)과 같은 별도의 모듈에 정의 된 유형에 대해서는 불가능한 드문 경우에 type ... and ...을 사용하여 상호 종속 유형을 만들 수 있습니다.

동료 F # 프로그래머는 어떤 접근 방식을 취합니까? 내가 명백한 (또는 그렇게 명백하지 않은) 것을 간과하고 있는지 궁금해한다. 그렇지 않다면 내부에있는 해당 유형과 동일한 이름을 모듈에 지정할 수없는 문제를 다루는 규칙이 있는지 궁금하다. 동일한 네임 스페이스.

답변

9

나열되지 않은 네 번째 방법은 유형과 모듈의 이름을 동일하게 지정하는 것입니다. 할 수는 없다고 생각하지만 실제로는 매우 일반적인 습관입니다. 모듈의 [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 속성을 사용하기 만하면됩니다 (4.1 이전의 F # 버전). 모든 것을 쓰는 것은 약간 추한 일입니다. 따라서 모듈과 같은 이름을 가진 타입을 정의하면 F# 4.1 made this the default behavior이됩니다. 어떻게 완료되었는지 확인하려면 FSharpx.Collections 코드 (다른 많은 프로젝트들 중에서)를보십시오.

namespace FSharpx.Collections 

type Queue<'T> (front : list<'T>, rBack : list<'T>) = 
    // ... 
    member this.Conj x = 
     match front, x::rBack with 
     | [], r -> Queue((List.rev r), []) 
     | f, r -> Queue(f, r) 
    // ... 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 
module Queue = 
    // ... 
    let inline conj (x : 'T) (q : Queue<'T>) = (q.Conj x) 
    // ... 

같은 이름의 종류와 모듈, 코드 이런 식으로 구성

https://github.com/fsprojects/FSharpx.Collections/blob/master/src/FSharpx.Collections/Queue.fs의 F 번호 : 여기에 좋은 예를 들면하게 너무 엄청나게 큰 아닌 하나 개의 파일입니다 컴파일러는 완벽하게 상황을 바르게 유지할 수 있습니다. 특히 PascalCase 스타일로 이름이 지정되었지만 camelCase 스타일로 명명 된 모듈에 함수가있는 멤버 메서드를 사용하면 표준 명명 규칙을 따르는 경우 특히 그렇습니다. C# 컴파일러는 혼란 스러울 지 모르지만 CompilationRepresentation 속성이이를 처리하므로 다른 .Net 언어가 QueueModule이라는 모듈을 볼 수 있습니다.그래서 F에서 # :

C 번호에서
let q = new Queue([1;2;3], []) 
let moreItems = q |> Queue.conj 4 
let stillMoreItems = moreItems.Conj 5 

:

Queue<int> q = new Queue<int>({1,2,3}, {}); // Not valid list syntax, but whatever 
Queue<int> moreItems = QueueModule.Conj(4, q); 
Queue<int> stillMoreItems = moreItems.Conj(5); 

Queue.conj 기능은 F 번호에서 사용하는 것이 더 자연보고, 멤버 방법은 moreItems.Conj은 C#을에서 사용하는 것이 더 자연 보이지만, 모두에서 사용할 수 있습니다 두 언어 다.

+1

[F # 표준 라이브러리의 컬렉션 유형] (https://github.com/Microsoft/visualfsharp/blob/master/src/fsharp/FSharp.Core/list.fs)도 '동일한 모듈 형식으로 이름을 지정하십시오 (형식에 동등한 메서드가 첨부되어 있지는 않지만). –

+0

재미있는 선택, 해당 CompilationRepresentation 특성을 사용하여 (전에 본 적이없는, 팁 주셔서 감사합니다). C# 상호 운용성과 관련된 문제 (내게는 문제)가 있지만 내 질문에 대한 대단한 답변이므로이 대답의 확인 표시를 제공합니다. 기본적으로 나와있는 세 가지 방법 중 두 번째 방법과 같습니다. . 또한 멤버 메서드에 기능을 코딩하지 않아도됩니다. 나는 해당 유형의 모듈을 선호하는데, 왜냐하면 내 유형을 단순히 속성 선언에만 제한하고 (어떤 함수도 그들에 붙어 있지 않기 때문에) 선호하기 때문입니다. – MiloDC

+0

Queue가 C#을 관용적으로 만들고 싶은 레코드 유형 인 경우 호기심에서 벗어나 (필자는 내 함수를 내 형식과 이혼시키는 경향이 있으므로 필자는 레코드를 많이 사용합니다), 다음에 사용할 활성 패턴을 어떻게 정의할까요? 회원 방법? – MiloDC