2016-06-28 22 views
6

Tôi đang gặp sự cố khi DU của tôi hoạt động như mong đợi. Tôi đã xác định một DU mới mà một trong hai có một kết quả của loại < 'a> hoặc bất kỳ ngoại lệ bắt nguồn từ System.ExceptionF # Loại hình Công đoàn Phân biệt đối xử

open System 

// New exceptions. 
type MyException(msg : string) = inherit Exception(msg) 
type MyOtherException(msg : string) = inherit MyException(msg) 

// DU to store result or an exception. 
type TryResult<'a, 't> = 
    | Result of 'a 
    | Error of 't :> Exception 

//This is fine. 
let result = Result "Test" 

// This works, doing it in 2 steps 
let ex = new MyOtherException("Some Error") 
let result2 = Error ex 

// This doesn't work. Gives "Value Restriction" error. 
let result3 = Error (new MyOtherException("Some Error")) 

Tôi không thể hiểu tại sao nó lại được cho phép tôi để tạo ra một 'Lỗi' nếu Tôi làm điều đó trong 2 bước, nhưng khi tôi đang làm điều tương tự trên một dòng, tôi nhận được một lỗi Value Restriction.

Tôi đang thiếu gì?

Cảm ơn

CẬP NHẬT

Nhìn vào bài bởi @kvb, thêm loại thông tin mỗi khi tôi cần phải tạo ra một lỗi có vẻ hơi dài dòng, vì vậy tôi quấn nó lên thành một phương pháp bổ sung mà tạo Lỗi và hơi ngắn gọn hơn.

// New function to return a Result 
let asResult res : TryResult<_,Exception> = Result res 

// New function to return an Error 
let asError (err : Exception) : TryResult<unit,_> = Error(err) 

// This works (as before) 
let myResult = Result 100 

// This also is fine.. 
let myResult2 = asResult 100 

// Using 'asError' now works and doesn't require any explicit type information here. 
let myError = asError (new MyException("Some Error")) 

Tôi không chắc chắn nếu xác định Lỗi có 'đơn vị' sẽ có bất kỳ hậu quả nào mà tôi chưa lường trước được.

TryResult<unit,_> = Error(err) 

Trả lời

5

Hãy xem xét sự thay đổi nhẹ này:

type MyOtherException(msg : string) = 
    inherit MyException(msg) 
    do printfn "%s" msg 

let ex = new MyOtherException("Some Error") // clearly, side effect occurs here 
let result2 = Error ex // no side effect here, but generalized value 

let intResults = [Result 1; result2] 
let stringResults = [Result "one"; result2] // can use result2 at either type, since it's a generalized value 

let result3 = Error (MyOtherException("Some Error")) // result would be of type TryResult<'a, MyOtherException> for any 'a 

// In some other module in a different compilation unit 
let intResults2 = [Result 1; result3]  // why would side effect happen here? just using a generic value... 
let stringResults2 = [Result "one"; result3] // likewise here... 

Vấn đề là nó trông giống như result3 là một giá trị, nhưng các hệ thống kiểu .NET không hỗ trợ các giá trị chung, nó chỉ hỗ trợ các giá trị của các loại bê tông. Do đó, hàm tạo MyOtherException cần phải được gọi mỗi lần result3 được sử dụng; tuy nhiên, điều này sẽ dẫn đến bất kỳ tác dụng phụ nào xảy ra nhiều lần, điều này sẽ gây ngạc nhiên.Như Ringil gợi ý, bạn có thể giải quyết vấn đề này bằng cách yêu cầu trình biên dịch xử lý biểu thức dưới dạng giá trị:

[<GeneralizableValue>] 
let result3<'a> : TryResult<'a,_> = Error(new MyOtherException("Some Error")) 

Điều này là tốt miễn là hàm tạo không có tác dụng phụ.

+0

Cảm ơn. Điều này thật ý nghĩa. Tôi đã cập nhật câu hỏi của mình bằng cách thêm phương thức tạo Lỗi bổ sung có vẻ hoạt động tốt và giữ cho nó gọn gàng hơn một chút. Chỉ có câu hỏi là loại giờ đây được chỉ định là TryResult Không chắc chắn rằng loại này sẽ có bất kỳ bên trái nào. – Moog

2

Bạn có thể làm:

let result3<'a> = Error (new MyOtherException("Some Error")) 

EDIT:

Đối với lý do tại sao bạn không thể làm điều đó trong một bước, lưu ý đầu tiên mà kết quả này trong các lỗi tương tự:

let result4 = Result (new MyOtherException("Some Error")) 

Như vậy:

let result4 = Result ([|1;|]) 

Nhưng mà làm việc này:

let result4 = Result ([1;]) 

gì tương tự về ngoại lệ và Mảng, nhưng không Lists? Đó là khả năng biến đổi của chúng. Hạn chế giá trị sẽ làm phiền bạn khi bạn cố gắng thực hiện một TryResult với một loại có thể thay đổi được trong một bước. Bây giờ là lý do tại sao quá trình hai bước giải quyết điều này, đó là bởi vì các nhà xây dựng làm cho toàn bộ chức năng không generalizable bởi vì bạn đang áp dụng một chức năng cho các nhà xây dựng. Nhưng tách nó thành hai bước để giải quyết điều đó. Nó tương tự như Trường hợp 2 here on MSDN.

Bạn có thể đọc thêm về nó tại bài viết MSDN ở trên và lý do tại sao điều này xảy ra trong this more indepth blog post.

+0

Ok, cảm ơn. Đó là một công việc xung quanh. Bất kỳ ý tưởng tại sao nó đòi hỏi tham số bổ sung này trong khi thực hiện nó trong 2 bước không? – Moog

+0

Tôi đã thêm một số giải thích. – Ringil

+4

Nó không phải là mảng có thể thay đổi được nhưng danh sách không phải là - đó là về những gì được coi là một "biểu thức khái quát". Ví dụ, xem điều gì sẽ xảy ra nếu bạn thử 'Kết quả ([1] @ [2])' hoặc 'Kết quả [| |] '. Ngoài ra, cách giải quyết của bạn dường như không tốt với tôi; chữ ký kiểu kết quả là 'val result3 <'a>: TryResult ', không giống như mong muốn; nếu bạn đi tuyến đường này, bạn nên thêm chú thích kiểu. – kvb

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