2010-10-11 20 views
7

Tóm tắt Sự cốLàm cách nào để chuyển đổi hoàn toàn thành các loại siêu phổ biến trong các kết hợp mẫu F #?

Tại thời điểm này khi sử dụng f # Tôi phải ép buộc một cách rõ ràng giá trị cho các loại mẹ của loại của nó để có được mô hình phù hợp với biểu thức để gõ kiểm tra một cách chính xác. Tôi sẽ lý tưởng như một cách thức làm việc.

Ví dụ

Giả sử tôi có một số lớp hệ thống cấp bậc:

type Foo() = 
    abstract member Value : unit -> string 

type A (i:int) = 
    inherit Foo() 
     override this.Value() = i.ToString() 

type B (s:string) = 
    inherit Foo() 
     override this.Value() = s 

Lý tưởng nhất, và trong một số ngôn ngữ lập trình trong bình thường, tôi sẽ viết tương đương với các nội dung sau:

let bar (i:int) : Foo = 
    match i with 
     | 1 -> B "one" 
     | _ -> A i 

Tuy nhiên điều này không đánh máy kiểm tra chính xác, cho tôi lỗi, "Biểu hiện này được dự kiến ​​sẽ có loại Foo nhưng cô ấy e có loại B ". Tôi không hiểu tại sao trình biên dịch không có đủ thông tin để suy ra một loại siêu phổ biến cho biểu thức khớp và sau đó kiểm tra xem loại siêu phổ biến là 'Foo' hay không.

Hiện nay tôi buộc phải cung cấp một sự ép buộc rõ ràng cho mọi trường hợp trong mô hình phù hợp:

let bar2 (i:int) : Foo = 
    match i with 
     | 1 -> (B "one") :> Foo 
     | _ -> (A i) :> Foo 

Tôi muốn tránh điều này.

Ghi chú Hơn nữa

  • Trực giác cho thấy rằng đây là một kết quả của một vấn đề tổng quát hơn. Tôi đã có thể nghĩ rằng mặc dù một cái gì đó phổ biến như phù hợp với mô hình, hoặc nếu báo cáo mà cũng thể hiện cùng một tài sản, sẽ có một loại kiểm tra quy tắc để giải thích cho các loại siêu phổ biến.
  • Trước khi bất cứ ai gợi ý - Tôi đánh giá cao nếu A hoặc B là Biểu thức đối tượng thì điều này sẽ hiệu quả, nhưng ví dụ thực sự của tôi là tạo các thể hiện của các lớp C# nơi chúng là các lớp bình thường.
  • Có cách nào để tôi khai báo hàm để chuyển đổi các loại ngầm định, ví dụ như scala có, vì vậy tôi có thể áp dụng chuyển đổi tự động cho mô-đun mà tôi đang tạo thế hệ này không?

Cảm ơn bạn đã giúp đỡ về vấn đề này.

Trả lời

7

Tôi sẽ sử dụng upcast, a la

[<AbstractClass>] 
type Foo() = 
    abstract member Value : unit -> string 

type A (i:int) = 
    inherit Foo() 
    override this.Value() = i.ToString() 

type B (s) = 
    inherit Foo() 
    override this.Value() = s 

let bar2 i : Foo = 
    match i with 
    | 1 -> upcast B "one" 
    | _ -> upcast A i 

Bạn vẫn phải thêm nó vào mỗi chi nhánh, nhưng điều này thường thích hợp hơn để đúc các loại, vì thường là typename là như dài 20 hoặc 30 ký tự (MyNamespace.ThisThingy), trong khi upcast chỉ là 6 ký tự.

Nhưng, một thời gian ngắn, các quy tắc ngôn ngữ không cho phép bất kỳ điều gì khác, các loại của tất cả các nhánh phải bằng nhau.

+2

Lý do trình kiểm tra loại không thực hiện bất kỳ hành vi phạm tội nào với các kiểu dẫn xuất là trước tiên nó sẽ làm cho bước kiểm tra kiểu phức tạp hơn và thứ hai nó cho phép bạn giới thiệu các lỗi tinh vi. Ví dụ: hãy xem xét các điều sau: khớp với i với | 1 -> B ("một") | 2 -> "stuff" Vâng, cả hai System.String và B chia sẻ chung loại cơ sở System.Object, do đó kết quả của biểu thức khớp đó là loại obj. Đó là 99,9% không phải những gì bạn muốn. –

+1

Đúng, mặc dù trong trường hợp cụ thể mà OP hỏi, anh ta đã khai báo kiểu trả về hàm là ': Foo', vì vậy tôi nghĩ có lẽ sẽ không hoàn toàn vô lý đối với người trợ giúp để sử dụng nó như là một mức thấp nhất bị ràng buộc hoặc một cái gì đó. Tuy nhiên, tôi đã không suy nghĩ qua tất cả các hàm ý của điều đó. – Brian

+1

Tôi nghĩ rằng 'x:> _' đọc tốt hơn' upcast x', nhưng có lẽ đó chỉ là tôi. Các chi nhánh của trận đấu không xếp hàng là tốt, mặc dù. – kvb

5

Tôi đã nhìn thấy câu hỏi này một vài lần trước đây, nhưng tôi mới nhận ra rằng có một cách khá thú vị để giải quyết vấn đề (không có bất kỳ tác động tiêu cực đáng kể nào như phí thời gian chạy lớn).

Bạn có thể sử dụng biểu thức tính toán rất đơn giản chỉ có Return thành viên. Trình tạo sẽ có thông số loại và Return sẽ mong đợi các giá trị thuộc loại này. Bí quyết là, rằng F # thực hiện chèn tự động phát khi gọi thành viên. Đây là tuyên bố:

type ExprBuilder<'T>() = 
    member x.Return(v:'T) = v 

let expr<'T> = ExprBuilder<'T>() 

Để viết một mô hình kết hợp đơn giản mà trả về bất cứ điều gì như obj, bây giờ bạn có thể viết:

let foo a = expr<obj> { 
    match a with 
    | 0 -> return System.Random() 
    | _ -> return "Hello" } 

Bạn vẫn phải được rõ ràng về các kiểu trả về (khi tạo biểu thức tính toán), nhưng tôi thấy cú pháp khá gọn gàng (nhưng nó chắc chắn là một cách sử dụng khéo léo có thể gây nhầm lẫn cho những người sẽ thấy nó lần đầu tiên).

+0

Đây là một giải pháp thú vị, nhưng tôi vẫn phải có cú pháp trên mọi dòng của đối sánh mẫu. Tuy nhiên - đây chắc chắn là giải pháp tốt nhất mà tôi từng thấy! –

+2

-1, theo ý kiến ​​của tôi, điều này "quá thông minh". – Brian

+0

@Brian: Tôi sẽ coi đó như là một lời khen :-), nhưng bạn nói đúng, nó có lẽ quá phức tạp (và tôi đã viết câu đó trong câu cuối) mặc dù nó luôn phụ thuộc vào loại dự án - Có phải là một số C#/F # hệ thống ống nước hoặc một phần cốt lõi của một hệ thống F # phức tạp? Trong một số trường hợp, các giải pháp khôn lanh nên được cho phép :-). Và sau tất cả, luồng công việc _asynchronous_ cũng phức tạp (theo nghĩa nào đó). –

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