2016-10-25 15 views
7

Tôi hiếm khi có cuộc đấu tranh này ngày nay với F #, nhưng sau đó nhập lại kiểu thừa kế ít phổ biến hơn với F #, vì vậy có lẽ tôi đã may mắn. Hoặc tôi thiếu điều hiển nhiên. Thông thường khi trình biên dịch phàn nàn về việc không biết một loại nào đó tôi đảo ngược thứ tự của các đường ống hoặc toán hạng thành phần và tôi đã hoàn thành.Loại suy luận với đường ống hoặc thành phần không thành công, nơi mà cuộc gọi hàm bình thường thành công

Về cơ bản, với một cuộc gọi chức năng hoạt động như g(f x), nó cũng hoạt động như x |> f |> g hoặc (f >> g) x. Nhưng hôm nay nó không ...

Dưới đây là một bằng chứng-of-concept lộn xộn của những gì tôi có nghĩa là:

module Exc = 
    open System 

    type MyExc(t) = inherit Exception(t) 

    let createExc t = new MyExc(t) 
    type Ex = Ex of exn 
    type Res = Success of string | Fail of Ex with 
     static member createRes1 t = Ex(createExc(t)) |> Fail // compiled 
     static member createRes2 t = t |> createExc |> Ex |> Fail // FS0001 
     static member createRes3 = createExc >> Ex >> Fail // FS0001 

Thông thường, các công trình này (ít nhất là trong kinh nghiệm của tôi). Các đường có lỗi "không thành công":

lỗi FS0001: Loại không khớp. Mong đợi một MyExc -> 'a nhưng đưa ra một exn -> Ex. Loại 'MyExc' không khớp với loại 'exn'

Không phải là một vấn đề lớn, không khó giải quyết, nhưng tôi phải viết nhiều mã trong đó bố cục là cách tiếp cận dễ dàng/sạch hơn và tôi không muốn viết một loạt các chức năng tiện ích mà tôi phải đặt ở mọi nơi.

Tôi đã xem loại linh hoạt, vì tôi đoán đây là một vấn đề về ngoại cảm, nhưng tôi không thấy cách tôi có thể áp dụng tại đây. Bất kỳ ý tưởng để giữ thành ngữ này?

Lưu ý, nếu tôi sắp xếp lại, tức là Ex <<createExc>> Fail hoặc sử dụng toán tử đường dẫn ngược, tôi sẽ gặp lỗi tương tự ở một phần khác.

Trả lời

7

Trình biên dịch F # hoạt động hơi không đều trong trường hợp này. Trong ví dụ của bạn, bạn muốn chuyển một giá trị kiểu MyExc cho một hàm tạo dự kiến ​​exn. Xử lý một đối tượng như là một giá trị của lớp cơ sở của nó là một sự kết hợp hợp lệ, nhưng trình biên dịch F # chèn các liên kết như vậy ở những nơi rất hạn chế.

Cụ thể, nó chèn coersion khi bạn chuyển đối số cho hàm, nhưng nó không chèn chúng (ví dụ) khi tạo danh sách hoặc trả về kết quả từ hàm.

Trong ví dụ của bạn, bạn cần có sự kết hợp khi truyền giá trị cho một hàm tạo công đoàn phân biệt đối xử. Có vẻ như rằng điều này chỉ xảy ra khi trực tiếp tạo ra các trường hợp công đoàn, nhưng nó không xảy ra khi điều trị các trường hợp công đoàn là một hàm:

// foo is a function that takes `obj` and Foo is a DU case that takes `obj` 
let foo (o:obj) = o 
type Foo = Foo of obj 

foo(System.Random()) // Coersion inserted automatically 
Foo(System.Random()) // Coersion inserted automatically 

System.Random() |> foo // Coersion inserted automatically 
System.Random() |> Foo // ..but not here! 

Vì vậy, các thiết lập hạn chế về nơi F # biên dịch áp dụng coersions tự động bao gồm các cách gọi khác nhau, nhưng chỉ có cách trực tiếp để tạo các trường hợp DU.

Đây là một hành vi buồn cười - và tôi nghĩ rằng điều này có ý nghĩa khi xử lý các trường hợp DU như các chức năng thông thường bao gồm chèn tự động các liên kết khi bạn sử dụng |>, nhưng tôi không chắc chắn có bất kỳ lý do kỹ thuật nào không làm cho điều đó khó khăn.

+0

Tuyệt vời và sâu sắc, như mọi khi, cảm ơn Tomas. Có vẻ như chúng tôi đồng ý rằng đây chắc chắn là một loại hành vi kỳ quặc. Tôi luôn luôn nghĩ (và thông thường đó là chính xác) mà các nhà xây dựng DU hành xử như các hàm và tôi chuỗi/soạn chúng tất cả các thời gian. Nhìn vào chữ ký của các thành viên DU, họ "trông giống như" chức năng. – Abel

2

Bạn có thể tạo các loại chung chung với ràng buộc thừa kế.

open System 

type MyExc (t) = inherit Exception (t) 

let createExc t = MyExc (t) 
type Ex<'t when 't :> exn> = Ex of 't 
type Res<'t when 't :> exn> = Success of string | Fail of 't Ex with 
    static member createRes1 t = Ex (createExc t) |> Fail 
    static member createRes2 t = t |> createExc |> Ex |> Fail 
    static member createRes3 = createExc >> Ex >> Fail 
+0

Vâng, đó là một giải pháp khả thi (giả sử các loại có thể truy cập). Tôi đã hy vọng hiểu rõ hơn tại sao 'f x' thành công, nhưng' x |> f' thì không, hoặc làm sao để cả hai thành công. Toán tử '|>' được gạch chân, vì vậy chắc chắn các cuộc gọi này phải tương đương? – Abel

3

Loại suy luận không hoạt động tốt với loại phụ (trong đó quyền thừa kế là một trường hợp).Thuật toán H & M không có khái niệm về phân loại trong đó, và các nỗ lực khác nhau để thích nghi nó theo thời gian không mang lại kết quả tốt. Trình biên dịch F # cố gắng hết sức để thích ứng với subtyping nơi nó có thể, dưới dạng các bản vá lỗi đặc biệt. Ví dụ, nó sẽ xem xét chức năng "tương thích" khi đối số thực tế là một supertype của tham số chính thức. Nhưng vì lý do nào đó, "bản vá" này không dịch khi chuyển đổi các hàm tạo công đoàn thành các hàm.

Ví dụ:

type U() = inherit exn() 
type T = T of exn 

let g f x = f x 

let e = U() 
let a = T e  // works 
let b = g T e  // compile error: `e` was expected to have type `exn`, but here has type `U` 

Trên dòng cuối cùng, đoàn constructor T được sử dụng như một chức năng miễn phí, vì vậy nó mất vá subtyping.

Tò mò đủ, công trình này cho các chức năng thường xuyên (tức là những người mà đã không bắt đầu ra như nhà xây dựng công đoàn):

let makeT u = T u 
let a = makeT e  // works 
let b = g makeT e // also works! 

Và nó thậm chí hoạt động điểm miễn phí:

let makeT = T 
let a = makeT e  // works 
let b = g makeT e // still works! 

Chi tiết này gợi ý giải pháp cho bạn: bạn chỉ có thể đặt một tên khác cho nhà xây dựng Ex và đường ống sẽ hoạt động:

type Ex = Ex of exn 
let makeEx = Ex 

    static member createRes2 t = t |> createExc |> makeEx |> Fail // Should work now 
Các vấn đề liên quan