2010-11-13 29 views
15

F # đang cho tôi một số rắc rối với các quy tắc suy luận kiểu của nó. Tôi đang viết một trình xây dựng tính toán đơn giản nhưng không thể có được các ràng buộc biến kiểu chung của tôi ngay.Làm cách nào để dịch một ràng buộc tham số kiểu `chung T: U` từ C# thành F #?


Mã mà tôi muốn trông như sau trong C#:

class FinallyBuilder<TZ> 
{ 
    readonly Action<TZ> finallyAction; 

    public FinallyBuilder(Action<TZ> finallyAction) 
    { 
     this.finallyAction = finallyAction; 
    } 

    public TB Bind<TA, TB>(TA x, Func<TA, TB> cont) where TA : TZ 
    {          //  ^^^^^^^^^^^^^ 
     try        // this is what gives me a headache 
     {         //  in the F# version 
      return cont(x); 
     } 
     finally 
     { 
      finallyAction(x); 
     } 
    } 
} 

tốt nhất (nhưng không biên dịch mã) Tôi đã đưa ra cho F # phiên bản cho đến thời điểm này là:

type FinallyBuilder<′z> (finallyAction : ′z -> unit) = 

    member this.Bind (x : ′a) (cont : ′a -> ′b) = 
     try  cont x 
     finally finallyAction (x :> ′z) // cast illegal due to missing constraint 

// Note: ' changed to ′ to avoid bad syntax highlighting here on SO. 

Unfortu nately, tôi không có đầu mối làm thế nào tôi sẽ dịch các ràng buộc loại where TA : TZ loại phương pháp Bind. Tôi nghĩ rằng nó phải là một cái gì đó như ′a when ′a :> ′z, nhưng trình biên dịch F # không thích điều này bất cứ nơi nào và tôi luôn luôn kết thúc với một số loại biến chung chung hạn chế khác.

Có thể ai đó vui lòng cho tôi xem mã F # chính xác không?


Bối cảnh: Mục tiêu của tôi là để có thể viết một F workflow # tùy chỉnh như thế này:

let cleanup = new FinallyBuilder (fun x -> ...) 

cleanup { 
    let! x = ... // x and y will be passed to the above lambda function at 
    let! y = ... // the end of this block; x and y can have different types! 
} 

Trả lời

8

Tôi không nghĩ rằng có thể viết ràng buộc như thế này trong F # (mặc dù tôi không chắc chắn chính xác lý do tại sao). Dù sao, syntacticalaly, bạn muốn viết một cái gì đó như thế này (như Brian gợi ý):

type FinallyBuilder<'T> (finallyAction : 'T -> unit) = 
    member this.Bind<'A, 'B when 'A :> 'T>(x : 'A) (cont : 'A -> 'B) = //' 
    try cont x 
    finally finallyAction (x :> 'T) 

Unfortunatelly, điều này mang đến cho các lỗi sau:

error FS0698: Invalid constraint: the type used for the constraint is sealed, which means the constraint could only be satisfied by at most one solution

Điều này dường như là trường hợp tương tự như một thảo luận trong this mailing list. Trường hợp Don Syme nói như sau:

This is a restriction imposed to make F# type inference tractable. In particular, the type on the right of a subtype constraint must be nominal. Note constraints of the form 'A :> 'B are always eagerly solved to 'A = 'B, as specified in section 14.6 of the F# specification.

Bạn luôn có thể giải quyết điều này bằng cách sử dụng obj trong chức năng được truyền cho người xây dựng của bạn.
EDIT: Ngay cả khi bạn sử dụng obj, các giá trị ràng buộc sử dụng let! sẽ có các loại cụ thể hơn (khi gọi finallyAction, F # sẽ tự động cast giá trị của một số tham số kiểu để obj):

type FinallyBuilder(finallyAction : obj -> unit) = 
    member x.Bind(v, f) = 
    try f v 
    finally finallyAction v 
    member x.Return(v) = v 

let cleanup = FinallyBuilder(printfn "%A") 

let res = 
    cleanup { let! a = new System.Random() 
      let! b = "hello" 
      return 3 } 
+1

OK, tôi khá thuyết phục khi không có giải pháp gọn gàng cho những gì tôi muốn làm. Việc chỉ định 'obj' cho' finallyAction' có tác dụng phụ khó chịu khi giảm tất cả các giá trị ràng buộc tùy chỉnh ('let!') Trong luồng công việc tùy chỉnh thành 'obj', có nghĩa là tôi không thể thực hiện công việc hợp lý với chúng bất kỳ lâu hơn. Sẽ cần phải suy nghĩ một số chi tiết về cách triển khai trình tạo đó khác nhau. Nhưng tôi hy vọng họ sẽ sửa lỗi này trong phiên bản tương lai của ngôn ngữ F # ... – stakx

+1

@stakx: Ngay cả khi bạn sử dụng 'obj', loại giá trị được ràng buộc bằng cách sử dụng' let! 'Phải là loại thực tế (cụ thể hơn) . Tôi đã chỉnh sửa câu trả lời để bao gồm một ví dụ minh họa điều này (cuối cùng tôi đã thử nghiệm nó :-)). –

+0

bạn là một thuật sĩ! :) Tôi đoán tất cả những chú thích kiểu này đã bị cản trở. Cảm ơn! – stakx

3

Nó sẽ là một cái gì đó giống như

...Bind<'A when 'A :> 'Z>... 

nhưng hãy để tôi mã nó để đảm bảo đúng như vậy ...

Ah, giao diện giống như nó sẽ là thế này:

type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
    member this.Bind<'a, 'b when 'a :> 'z> (x : 'a, cont : 'a -> 'b) : 'b = 
     try  cont x 
     finally finallyAction x //(x :> 'z)// illegal 

ngoại trừ việc

http://cs.hubfs.net/forums/thread/10527.aspx

chỉ ra rằng F # không làm contraints dạng "T1:> T2", nơi cả hai đều biến kiểu (nó giả T1 = T2). Tuy nhiên, điều này có thể đúng đối với trường hợp của bạn, bạn đã định sử dụng chính xác những gì như là các sự kiện cụ thể của Z? Có lẽ có một cách giải quyết đơn giản hoặc một số mã ít chung chung hơn sẽ đáp ứng kịch bản. Ví dụ, tôi tự hỏi, nếu công trình này:

type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
    member this.Bind<'b> (x : 'z, cont : 'z -> 'b) : 'b = //' 
     try  cont x 
     finally finallyAction x 

Dường như với:

type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
    member this.Bind<'b> (x : 'z, cont : 'z -> 'b) : 'b = // ' 
     try  cont x 
     finally finallyAction x 
    member this.Zero() =() 

[<AbstractClass>] 
type Animal() = 
    abstract Speak : unit -> unit 

let cleanup = FinallyBuilder (fun (a:Animal) -> a.Speak()) 

type Dog() = 
    inherit Animal() 
    override this.Speak() = printfn "woof" 

type Cat() = 
    inherit Animal() 
    override this.Speak() = printfn "meow" 

cleanup { 
    let! d = new Dog() 
    let! c = new Cat() 
    printfn "done" 
} 
// prints done meow woof 

Ồ, tôi nhìn thấy, nhưng dc bây giờ có loại Animal. Hm, hãy để tôi xem nếu có bất kỳ thông minh còn lại trong tôi ...

Vâng, rõ ràng là bạn có thể làm

type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
    member this.Bind<'a,'b> (x : 'a, cont : 'a -> 'b) : 'b = // ' 
     try  cont x 
     finally finallyAction (x |> box |> unbox) 
    member this.Zero() =() 

mà ném đi kiểu an toàn (sẽ ném một ngoại lệ cast khi chạy nếu điều này là không cuối cùngHành động).

Hoặc bạn có thể làm cho các nhà xây dựng loại cụ thể:

type FinallyBuilderAnimal (finallyAction : Animal -> unit) = 
    member this.Bind<'a,'b when 'a:>Animal>(x : 'a, cont : 'a -> 'b) : 'b = //' 
     try  cont x 
     finally finallyAction x 
    member this.Zero() =() 

let cleanup = FinallyBuilderAnimal (fun a -> a.Speak()) 

Nhưng tôi nghĩ tôi ra khỏi những ý tưởng thông minh khác.

+0

_ "Đây xây dựng nguyên nhân mã ít chung chung hơn so với chỉ định bởi các chú thích kiểu. Biến kiểu ''a' đã bị ràng buộc là kiểu'' z'. "_ :-( – stakx

+0

Cảm ơn câu trả lời của bạn. Tôi có thể giải quyết vấn đề của tôi bằng cách sửa một số biến kiểu cho một loại bê tông.Tôi chỉ là một chút thất vọng khi thấy rằng F #, trong đó có tất cả các suy luận kiểu tuyệt vời này, không thể làm điều gì đó mà C# ca n thật dễ dàng ... điều đó thật bất ngờ. – stakx

+0

_ "Nhưng tôi nghĩ tôi không có những ý tưởng thông minh khác." _ Tôi rất biết ơn những nỗ lực của bạn. Bạn phải đã bị hấp dẫn bởi điều này! ;-) Tôi đã chơi xung quanh với điều này cả ngày và đã không tìm thấy một giải pháp dễ chịu, vì vậy nhìn thấy những người khác đấu tranh với điều này ít nhất là trấn an tôi rằng tôi đã không bỏ lỡ một cái gì đó rất cơ bản. – stakx

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