2015-12-10 25 views
6

Tôi yêu sự đơn giản của các loại nhưLoại để đại diện cho một chuỗi mà không phải là rỗng hoặc khoảng trống trong F #

type Code = Code of string 

Nhưng tôi muốn đặt một số hạn chế về chuỗi (trong trường hợp này - không cho phép trống các chuỗi chỉ dấu cách). Một cái gì đó như

type nonemptystring = ??? 
type Code = Code of nonemptystring 

Làm cách nào để xác định loại này theo cách thành ngữ F #? Tôi biết tôi có thể làm cho nó một lớp với constructor hoặc một mô-đun bị hạn chế với chức năng nhà máy, nhưng là có một cách dễ dàng?

+3

Hãy xem bài đăng này: http://fsharpforfunandprofit.com/posts/designing-with-types-more-semantic-types/ - phần "Lập mô hình chuỗi bị ràng buộc với các loại". Bạn có thể sẽ cần phải làm điều gì đó như thế này – Petr

+0

@Petr Tôi thấy bài đăng này nhưng tôi thấy giải pháp này hơi phức tạp. Cảm ơn bạn đã liên kết. – Mikhail

+0

Có, tôi đồng ý rằng bài đăng đó hơi phức tạp một chút. Tôi có một số ví dụ tốt hơn ở đây: https://gist.github.com/swlaschin/54cfff886669ccab895a. Như những người khác đã chỉ ra, các giải pháp không đặc biệt thanh lịch. OTOH, bạn cũng phải làm điều tương tự trong mã OO. – Grundoon

Trả lời

0

Rất tiếc, không có cú pháp thuận tiện để khai báo một tập hợp con các loại bị hạn chế nhưng tôi sẽ tận dụng các mẫu hoạt động để thực hiện việc này. Như bạn nói đúng, bạn có thể tạo một loại và kiểm tra tính hợp lệ của nó khi bạn xây dựng nó:

/// String type which can't be null or whitespace 
type FullString (string) = 
    let string = 
     match (System.String.IsNullOrWhiteSpace string) with 
     |true -> invalidArg "string" "string cannot be null or whitespace" 
     |false -> string 
    member this.String = string 

Bây giờ, việc xây dựng kiểu này có thể ném ngoại lệ thời gian chạy và chúng tôi không muốn điều đó! Vì vậy, hãy sử dụng các mẫu hoạt động:

let (|FullStr|WhitespaceStr|NullStr|) (str : string) = 
    match str with 
    |null -> NullStr 
    |str when System.String.IsNullOrWhiteSpace str -> WhitespaceStr 
    |str -> FullStr(FullString(str)) 

Bây giờ chúng ta có một cái gì đó mà chúng ta có thể sử dụng với cú pháp khớp mẫu để xây dựng FullString s của chúng tôi. Chức năng này an toàn khi chạy vì chúng tôi chỉ tạo một FullString nếu chúng tôi đang trong trường hợp hợp lệ.

Bạn có thể sử dụng nó như thế này:

let printString str = 
    match str with 
    |NullStr -> printfn "The string is null" 
    |WhitespaceStr -> printfn "The string is whitespace" 
    |FullStr fstr -> printfn "The string is %s" (fstr.String) 
+0

Cảm ơn ví dụ này. Tôi có hai mối quan tâm: 1. Tôi sẽ phải sử dụng ".String" mỗi khi tôi sử dụng nó ở đâu đó có vẻ vụng về. 2. Tôi sẽ phải ghi đè Equals và những gì không cho loại FullString này. – Mikhail

+0

@Mikhail Bạn có thể sử dụng một bản ghi thay vì một lớp mà sẽ cung cấp cho bạn sự so sánh cấu trúc tự động, bình đẳng, vv nhưng bạn phải thực hiện một chút thủ thuật để đảm bảo bản ghi không bao giờ chứa chuỗi không hợp lệ. Điều đó nói rằng, các ghi đè này nên khá đơn giản vì bạn chỉ có thể chuyển hướng trực tiếp đến các phương thức của chuỗi. – TheInnerLight

3

Một string về cơ bản là một chuỗi các char giá trị (trong Haskell, BTW, String một loại bí danh cho [Char]). Một câu hỏi chung hơn, sau đó, sẽ là nếu nó có thể tĩnh khai báo một danh sách như có một kích thước nhất định.

Tính năng ngôn ngữ như vậy được biết là Dependent Types và F # không có. Câu trả lời ngắn, do đó, là điều này là không thể làm trong một thời trang khai báo.

Cách dễ dàng nhất, và có lẽ cũng thành ngữ nhất, bằng cách nào, sau đó, sẽ được xác định Code dưới dạng đĩa đơn hợp kỳ thị Union:

type Code = Code of string 

Trong module định nghĩa Code, bạn cũng muốn xác định một chức năng mà khách hàng có thể sử dụng để tạo ra giá trị Code:

let tryCreateCode candidate = 
    if System.String.IsNullOrWhiteSpace candidate 
    then None 
    else Some (Code candidate) 

chức năng này chứa logic thời gian chạy có thể ngăn chặn khách hàng từ việc tạo ra sản phẩm nào Code giá trị:

> tryCreateCode "foo";; 
val it : Code option = Some (Code "foo") 
> tryCreateCode "";; 
val it : Code option = None 
> tryCreateCode " ";; 
val it : Code option = None 

Điều gì ngăn cản khách hàng tạo giá trị Code không hợp lệ? Ví dụ: không phải khách hàng có thể phá vỡ chức năng tryCreateCode và chỉ cần viết Code ""?

Đây là nơi có signature files.Bạn có thể tạo một tập tin chữ ký (.fsi), và trong đó khai báo các loại và chức năng như thế này:

type Code 
val tryCreateCode : string -> Code option 

Ở đây, loại Code được khai báo, nhưng 'constructor' của nó không phải là. Điều này có nghĩa là bạn không thể tạo trực tiếp các giá trị của loại này. Này, ví dụ, không biên dịch:

Code "" 

Các lỗi được đưa ra là:

lỗi FS0039: Giá trị, xây dựng, không gian tên hoặc gõ 'Mã' không được định nghĩa

Cách duy nhất để tạo giá trị Code là sử dụng chức năng tryCreateCode.

Như đưa ra ở đây, bạn không còn có thể truy cập vào các chuỗi giá trị cơ bản của Code, trừ khi bạn cũng cung cấp một chức năng cho rằng:

let toString (Code x) = x 

và khai báo nó trong cùng một tập tin .fsi như trên:

val toString : Code -> string 

Điều đó có thể trông giống như rất nhiều công việc, nhưng thực sự chỉ có sáu dòng mã và ba dòng khai báo kiểu (trong tệp .fsi).

+1

Sẽ không làm cho các nhà xây dựng kiểu tư nhân là một thay thế? 'type Code = private Code of string' –

+0

@NikosBaxevanis Tôi không biết bạn có thể làm điều đó: $ Cảm ơn bạn! Điều đó phân phối với sự cần thiết cho một tập tin '.fsi', nhưng phần còn lại của câu trả lời của tôi vẫn được áp dụng. –

Các vấn đề liên quan