2013-08-30 10 views
14

F # ułatwia zdefiniowanie typów, takich jakJak zrobić walidację argument F # zapisów

type coords = { X : float; Y : float } 

ale w jaki sposób zdefiniować ograniczeń/check argumenty konstruktora bez wchodzenia składni definicji klasy bardziej opisowym? Na przykład. jeśli chcę, aby współrzędne zaczynały się od (0,0) lub generowały wyjątek.

Co więcej, jeśli zmienię moją definicję na klasę, muszę zaimplementować Equals() itd. Wszystkie kody płyty kotła, których nie chcę (i które mam w C#, które próbuję uciec) .

+1

możliwy duplikat [Czy możliwe jest wyegzekwowanie, że rekord respektuje niektóre niezmienniki?] (Http://stackoverflow.com/questions/13925361/is-it-possible-to-enforce-at-a-record-respects -some-invariants) –

+0

To jest duplikat drugiego pytania. Głosuj, aby zamknąć. –

Odpowiedz

13

można dokonać realizacja prywatne. Nadal masz strukturalną równość, ale tracisz bezpośredni dostęp do pola i dopasowywanie wzorców. Możesz przywrócić tę umiejętność za pomocą aktywnych wzorów.

//file1.fs 

type Coords = 
    private { 
    X: float 
    Y: float 
    } 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] 
module Coords = 
    ///The ONLY way to create Coords 
    let create x y = 
    check x 
    check y 
    {X=x; Y=y} 

    let (|Coords|) {X=x; Y=y} = (x, y) 

//file2.fs 

open Coords 
let coords = create 1.0 1.0 
let (Coords(x, y)) = coords 
printfn "%f, %f" x y 
+2

To dobre rozwiązanie problemu Daniel. Chciałbym wymyślić to. –

+3

Dzięki, moje przeczucie mówi mi, że to bardzo zawiłe i nietypowe podejście do robienia typów? Zgaduję, że tworzenie klasy i wdrażanie równań jest lepsze .. lub wymuszenie, że wszystkie typy w moim programie są konstruowane za pomocą jakiejś generycznej metody tworzenia? –

+0

To rzadkie, tak, ale tylko tak zawiłe jak to się robi. Myślę, że prywatna implementacja jest dobrą cechą i przydatna w niektórych, wprawdzie nieprzeciętnych scenariuszach. – Daniel

4

Musisz użyć składni definicji klasy:

type coords(x: float, y: float) = 
    do 
    if x < 0.0 then 
     invalidArg "x" "Cannot be negative" 
    if y < 0.0 then 
     invalidArg "y" "Cannot be negative" 

    member this.X = 
    x 
    member this.Y = 
    y 
+2

jeśli zmienię moją definicję na klasę potrzebuję wprowadzić Equals() itd. Wszystkie kody tablicy kotła, których nie chcę (i które mam w C#, które próbuję uciec). –

+0

Tak, musisz to zrobić. Jeśli chcesz obiektowo zorientować się w enkapsulacji/ukrywaniu danych, potrzebujesz obiektów podobnych do obiektów podobnych do C#, nie ma mowy o tym. Fajną rzeczą w F # jest to, że MOŻESZ to zrobić. – MiMo

+4

więc jaki byłby sposób F #? zdefiniować klasę lub zdefiniować funkcję tworzenia w tym samym module co rekord i mam nadzieję, że ludzie używają metody create? –

5

Jest to seria o nazwie Designing with Types na F# for fun and profit. W sekcji "Wymuszanie użycia konstruktora" zalecane jest użycie funkcji konstruktora - tutaj sprawdzanie poprawności następuje przed utworzeniem instancji typu. Aby powstrzymać ludzi przed bezpośrednim tworzeniem typów, zaleca się konwencje nazewnictwa lub pliki sygnatur.

Możesz znaleźć kilka bardziej odpowiednich artykułów i przykładów, korzystając z wyszukiwarki Google "design oparty na domenie f #".

Zauważ, że pochodzę z języka C#/nie stosuję F # do naszej warstwy domeny (jeszcze;) Nie mogę powiedzieć, w jaki sposób jedna z zalecanych metod zadziałałaby w większym projekcie. Niektóre rzeczy na pewno wyglądają inaczej w tym nowym, odważnym świecie.