2010-02-06 14 views
16

W jaki sposób decydujesz między pisaniem funkcji wewnątrz modułu lub jako statyczny element jakiegoś typu?Organizacja kodu F: typy i moduły

Na przykład, w kodzie źródłowym F #, istnieje wiele rodzajów, które są zdefiniowane wraz z równie nazwie modułu, w następujący sposób:

type MyType = // ... 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 
module MyType = // ... 

Dlaczego nie można po prostu zdefiniować operacje jako statyczne członkowie typu MyType?

Odpowiedz

23

Oto kilka uwag na temat technicznych różnic.

Moduły mogą być "otwarte" pod (chyba że mają RequireQualifiedAccessAttribute). Oznacza to, że jeśli umieścić funkcje (F i G) w module (M), to można napisać

open M 
... F x ... G x ... 

natomiast z metodą statyczną, można zawsze napisać

... M.F x ... M.G x ... 

Module funkcje nie mogą być przeciążone. Funkcje w module są dozwolone, a funkcje let-bound nie zezwalają na przeciążanie. Jeśli chcesz być w stanie wywołać zarówno

X.F(someInt) 
X.F(someInt, someString) 

należy użyć member s o rodzaju, które działa tylko z „kwalifikowanych” rozmowy (np type.StaticMember(...) lub object.InstanceMember(...)).

(Czy są jakieś inne różnice? Nie mogę sobie przypomnieć.)

To są główne różnice techniczne, które wpływają na wybór jednego nad drugim.

Ponadto istnieje pewna tendencja w środowisku wykonawczym F # (FSharp.Core.dll) do korzystania z modułów tylko dla typów F # (które zwykle nie są używane podczas wykonywania współdziałania z innymi językami .Net) i metod statycznych dla interfejsów API które są bardziej neutralne językowo. Na przykład wszystkie funkcje z parametrami curry pojawiają się w modułach (funkcje curry nie są trywialne, aby wywołać je z innych języków).

+2

+1. Ostatni akapit to dobry punkt! –

+0

_ "(Czy istnieją inne różnice? Nie pamiętam.)", Pozwól mi spróbować: (1) Wytyczne F # nazywania wymagają statycznego członka klasy, aby rozpocząć od kapitału, modułu let-binding z małą literą; (2) typy mogą być rozszerzone (użyj składni 'z'), moduły nie mogą (niestety tak); (3) statyczne elementy typu są zwykle inicjowane po pierwszym dostępie do typu, wiązanie let modułu jest inicjowane znacznie wcześniej, często, gdy powiązany typ w tym samym zestawie jest dostępny (jest to przede wszystkim ważne dla członków lub powiązań, które nie są funkcjami) . – Abel

+0

I podobieństwo: (4) statyczne elementy nie mogą być przeciążone ani przy użyciu składni funkcji w stylu F # ('statyczny element x a b' nie może być przeciążony,' statyczny element x (a, b) 'może). Ma to tę zaletę, że statyczne elementy mogą być również wykorzystywane w curry, ale mają tę wadę, że stają się trudniejsze do wywołania z C# itd. – Abel

3

W F # Wolę element statyczny od typu over funkcji w module, gdy ...

  1. muszę zdefiniować typ niezależnie od członka
  2. Człon jest funkcjonalnie związany z typ definiuję:
2

Oprócz innych odpowiedzi jest jeszcze jedna sprawa używać modułów:

Dla typów wartości mogą pomóc określić właściwości statyczne, które nie dostać ponownie oceniano za każdym razem są one dostęp. na przykład:

type [<Struct>] Point = 
    val x:float 
    val y:float 
    new (x,y) = {x=x;y=y} 

    static member specialPoint1 = // sqrt is computed every time the property is accessed 
     Point (sqrt 0.5 , sqrt 0.5) 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 
module Point = 

    let specialPoint2 = // sqrt is computed only once when the Module is opened 
     Point (sqrt 0.5 , sqrt 0.5) 
0

Niektóre duże różnice, które nie zostały pierwotnie wymienione:

  • Funkcje są wartościami pierwszej klasie w F #, ale nie są członkami statyczne.Możesz napisać: objs |> Seq.map Obj.func, ale nie możesz pisać objs |> Seq.map Obj.Member.

  • Funkcje można konwertować, ale członkowie nie mogą.

  • Kompilator automatycznie wnioskuje o typach podczas wywoływania funkcji, ale nie w przypadku wywoływania elementu. Możesz napisać: let func obj = obj |> Obj.otherFunc, ale nie możesz pisać let func obj = obj.Member.

Ponieważ członkowie są bardziej restrykcyjni, zwykle używam funkcji, chyba że wyraźnie chcę wspierać OOP/C#.