2010-10-27 31 views
40

Tôi đang cố dịch mũi tên của thư viện lõi Haskell thành F # (tôi nghĩ đó là một bài tập tốt để hiểu các Mũi tên và F # tốt hơn, và tôi có thể sử dụng chúng trong một dự án mà tôi đang làm việc). dịch trực tiếp là không thể do sự khác biệt trong mô hình. Haskell sử dụng các loại-class để thể hiện công cụ này, nhưng tôi không chắc chắn những gì F # xây dựng bản đồ tốt nhất các chức năng của loại-class với các thành ngữ của F #. Tôi có một vài suy nghĩ, nhưng đã tìm ra cách tốt nhất để đưa nó lên đây và xem những gì được coi là gần nhất trong chức năng.Làm cách nào để dịch lớp loại Haskell sang F #?

Đối với đám đông tl; dr: Làm cách nào để dịch các loại lớp (thành ngữ Haskell) thành mã # idiomatic?

Đối với những người chấp nhận lời giải thích dài của tôi:

Mã này từ tiêu chuẩn lib Haskell là một ví dụ về những gì tôi đang cố gắng để dịch:

class Category cat where 
    id :: cat a a 
    comp :: cat a b -> cat b c -> cat a c 
class Category a => Arrow a where 
    arr :: (b -> c) -> a b c 
    first :: a b c -> a (b,d) (c,d) 

instance Category (->) where 
    id f = f 
instance Arrow (->) where 
    arr f = f 
    first f = f *** id 

Cố gắng 1: Modules, Simple loại , Hãy Bindings

Ảnh đầu tiên của tôi ở đây là chỉ cần ánh xạ mọi thứ qua trực tiếp bằng Mô-đun cho tổ chức, như:

type Arrow<'a,'b> = Arrow of ('a -> 'b) 

let arr f = Arrow f 
let first f = //some code that does the first op 

Đó là công trình, nhưng nó bị mất tính đa hình, vì tôi không triển khai Danh mục và không thể triển khai dễ dàng nhiều Mũi tên chuyên biệt hơn.

Cố gắng 1a: Tinh chỉnh sử dụng chữ ký và các loại

Một cách để sửa chữa một số vấn đề với Cố gắng 1 là sử dụng một tập tin .fsi để xác định các phương pháp (vì vậy các loại thực thi dễ dàng hơn) và sử dụng một số đơn giản loại chỉnh để chuyên.

type ListArrow<'a,'b> = Arrow<['a],['b]> 
//or 
type ListArrow<'a,'b> = LA of Arrow<['a],['b]> 

Nhưng không thể sử dụng lại tệp fsi (để thực thi các loại hàm ràng buộc) cho các triển khai khác và loại đổi tên/đóng gói rất khó.

Cố gắng 2: mô hình đối tượng và giao diện

Hợp lý hoá mà F # được xây dựng để được OO cũng có, có thể là một hệ thống phân cấp loại là đúng cách để làm điều này.

type IArrow<'a,'b> = 
    abstract member comp : IArrow<'b,'c> -> IArrow<'a,'c> 
type Arrow<'a,'b>(func:'a->'b) = 
    interface IArrow<'a,'b> with 
     member this.comp = //fun code involving "Arrow (fun x-> workOn x) :> IArrow" 

Bên cạnh bao nhiêu một nỗi đau nó có thể để có được những gì cần được phương pháp tĩnh (như comp và nhà khai thác khác) để hoạt động như phương pháp dụ, cũng có nhu cầu upCast một cách rõ ràng các kết quả. Tôi cũng không chắc chắn rằng phương pháp này vẫn đang nắm bắt được sự thể hiện đầy đủ của đa hình loại lớp. Nó cũng làm cho nó khó khăn để sử dụng những thứ PHẢI là phương pháp tĩnh.

Cố gắng 2a: Tinh chỉnh sử dụng phần mở rộng loại

Vì vậy, thêm một sự tinh tế tiềm năng để khai báo các giao diện như trần càng tốt, sau đó sử dụng phương pháp mở rộng thêm chức năng cho tất cả các loại thực hiện.

type IArrow<'a,'b> with 
    static member (&&&) f = //code to do the fanout operation 

Ah, nhưng điều này khiến tôi sử dụng một phương pháp cho tất cả các loại IArrow.Nếu tôi muốn có một chút khác biệt (& & &) đối với ListArrows, tôi có thể làm gì? Tôi chưa thử phương pháp này, nhưng tôi đoán tôi có thể che bóng (& & &) hoặc ít nhất cung cấp phiên bản chuyên biệt hơn, nhưng tôi cảm thấy mình không thể thực thi việc sử dụng biến thể đúng.

Help me

Vì vậy, những gì tôi phải làm gì đây? Tôi cảm thấy như OO nên đủ mạnh để thay thế các loại lớp, nhưng tôi dường như không thể tìm ra cách để làm điều đó xảy ra trong F #. Có bất kỳ nỗ lực nào của tôi không? Có ai trong số họ "tốt như nó được" và đó sẽ phải là đủ tốt?

+5

"Làm cách nào để dịch các loại lớp (thành ngữ Haskell) thành mã # idiomatic?" Ý tưởng tồi. Bạn nên xem xét vấn đề * * mà bạn đang cố giải quyết thay vì giải pháp đã xảy ra để sử dụng các lớp kiểu và tìm ra cách giải quyết nó bằng cách sử dụng tính năng mà F # cung cấp. –

+16

@Jon Harrop Đó là điểm của câu hỏi. Haskell giải quyết hàng chục vấn đề với các loại lớp, và tôi muốn biết những gì các lựa chọn thay thế F # đã được giải quyết một lớp * tương tự của các vấn đề.Ngoài ra các cổng mũi tên không phải là để giải quyết bất kỳ vấn đề, nó chỉ là một "Tôi nghĩ rằng nó sẽ được vui vẻ để tìm hiểu thêm về mũi tên" hoạt động. – CodexArcanum

+1

Sau đó, tôi nghĩ rằng nó sẽ thực sự có giá trị nếu bạn có thể giải thích một số vấn đề mà các lớp học giải quyết và hỏi mọi người cách họ sẽ giải quyết các vấn đề tương tự trong F #. –

Trả lời

21

Dưới đây là cách tiếp cận tôi sử dụng để mô phỏng Typeclasses (từ http://code.google.com/p/fsharp-typeclasses/).

Trong trường hợp của bạn, ví Mũi tên có thể là một cái gì đó như thế này:

let inline i2 (a:^a,b:^b ) =              
    ((^a or ^b  ) : (static member instance: ^a* ^b  -> _) (a,b )) 
let inline i3 (a:^a,b:^b,c:^c) =               
    ((^a or ^b or ^c) : (static member instance: ^a* ^b* ^c -> _) (a,b,c)) 

type T = T with 
    static member inline instance (a:'a  ) = 
     fun x -> i2(a , Unchecked.defaultof<'r>) x :'r 
    static member inline instance (a:'a, b:'b) = 
     fun x -> i3(a, b, Unchecked.defaultof<'r>) x :'r 


type Return = Return with 
    static member instance (_Monad:Return, _:option<'a>) = fun x -> Some x 
    static member instance (_Monad:Return, _:list<'a> ) = fun x -> [x] 
    static member instance (_Monad:Return, _: 'r -> 'a) = fun x _ -> x 
let inline return' x = T.instance Return x 

type Bind = Bind with 
    static member instance (_Monad:Bind, x:option<_>, _:option<'b>) = fun f -> 
     Option.bind f x 
    static member instance (_Monad:Bind, x:list<_> , _:list<'b> ) = fun f -> 
     List.collect f x 
    static member instance (_Monad:Bind, f:'r->'a, _:'r->'b) = fun k r -> k (f r) r 
let inline (>>=) x (f:_->'R) : 'R = T.instance (Bind, x) f 
let inline (>=>) f g x = f x >>= g 

type Kleisli<'a, 'm> = Kleisli of ('a -> 'm) 
let runKleisli (Kleisli f) = f 

type Id = Id with 
    static member  instance (_Category:Id, _: 'r -> 'r ) = fun() -> id 
    static member inline instance (_Category:Id, _:Kleisli<'a,'b>) = fun() -> 
     Kleisli return' 
let inline id'() = T.instance Id() 

type Comp = Comp with 
    static member  instance (_Category:Comp,   f, _) = (<<) f 
    static member inline instance (_Category:Comp, Kleisli f, _) = 
     fun (Kleisli g) -> Kleisli (g >=> f) 

let inline (<<<) f g = T.instance (Comp, f) g 
let inline (>>>) g f = T.instance (Comp, f) g 

type Arr = Arr with 
    static member  instance (_Arrow:Arr, _: _ -> _) = fun (f:_->_) -> f 
    static member inline instance (_Arrow:Arr, _:Kleisli<_,_>) = 
     fun f -> Kleisli (return' <<< f) 
let inline arr f = T.instance Arr f 

type First = First with 
    static member  instance (_Arrow:First, f, _: 'a -> 'b) = 
     fun() (x,y) -> (f x, y) 
    static member inline instance (_Arrow:First, Kleisli f, _:Kleisli<_,_>) = 
     fun() -> Kleisli (fun (b,d) -> f b >>= fun c -> return' (c,d)) 
let inline first f = T.instance (First, f)() 

let inline second f = let swap (x,y) = (y,x) in arr swap >>> first f >>> arr swap 
let inline (***) f g = first f >>> second g 
let inline (&&&) f g = arr (fun b -> (b,b)) >>> f *** g 

Cách sử dụng:

> let f = Kleisli (fun y -> [y;y*2;y*3]) <<< Kleisli (fun x -> [ x + 3 ; x * 2 ]) ;; 
val f : Kleisli<int,int list> = Kleisli <fun:[email protected]> 

> runKleisli f <| 5 ;; 
val it : int list = [8; 16; 24; 10; 20; 30] 

> (arr (fun y -> [y;y*2;y*3])) 3 ;; 
val it : int list = [3; 6; 9] 

> let (x:option<_>) = runKleisli (arr (fun y -> [y;y*2;y*3])) 2 ;; 
val x : int list option = Some [2; 4; 6] 

> ((*) 100) *** ((+) 9) <| (5,10) ;; 
val it : int * int = (500, 19) 

> ((*) 100) &&& ((+) 9) <| 5 ;; 
val it : int * int = (500, 14) 

> let x:List<_> = (runKleisli (id'())) 5 ;; 
val x : List<int> = [5] 

Lưu ý: sử dụng id'() thay vì id

Cập nhật: bạn cần F # 3.0 để biên dịch mã này, nếu không here's the F# 2.0 version.

here's giải thích chi tiết về kỹ thuật này an toàn, có thể mở rộng và bạn có thể thấy các tác phẩm ngay cả với một số Typeclass loại cao cấp.

+1

Ngoài sự tò mò, F # 3.0 cụ thể với mã này là gì? –

+1

@GustavoGuerra Chức năng i3 sẽ không biên dịch trong F # 2.0 do lỗi trình phân tích cú pháp. Có một cách giải quyết bằng cách sử dụng toán tử, nhưng mã trở nên ít có thể đọc được. – Gustavo

+0

[http://www.nut-cracker.com.ar/index.php] ("giải thích chi tiết") không hoạt động. – Noein

29

Câu trả lời ngắn gọn của tôi là:

OO không đủ mạnh để thay thế các loại lớp.

Bản dịch đơn giản nhất là chuyển một từ điển hoạt động, như trong một triển khai điển hình cho kiểu chữ. Đó là nếu typeclass Foo xác định ba phương pháp, sau đó xác định một kiểu lớp/ghi lại tên Foo, và sau đó thay đổi chức năng của

Foo a => yadda -> yadda -> yadda 

chức năng như

Foo -> yadda -> yadda -> yadda 

và tại mỗi địa điểm cuộc gọi bạn biết bê tông 'Ví dụ' để chuyển dựa trên loại tại trang web gọi.

Dưới đây là một ví dụ ngắn về những gì tôi có nghĩa là:

// typeclass 
type Showable<'a> = { show : 'a -> unit; showPretty : 'a -> unit } //' 

// instances 
let IntShowable = 
    { show = printfn "%d"; showPretty = (fun i -> printfn "pretty %d" i) } 
let StringShowable = 
    { show = printfn "%s"; showPretty = (fun s -> printfn "<<%s>>" s) } 

// function using typeclass constraint 
// Showable a => [a] ->() 
let ShowAllPretty (s:Showable<'a>) l = //' 
    l |> List.iter s.showPretty 

// callsites 
ShowAllPretty IntShowable [1;2;3] 
ShowAllPretty StringShowable ["foo";"bar"] 

Xem thêm

https://web.archive.org/web/20081017141728/http://blog.matthewdoig.com/?p=112

+0

Hmmm ... Tôi nghĩ rằng tôi thấy một cái gì đó tương tự liên quan đến lớp F # Matrix, nơi nó có một từ điển của nói với ma trận mà các lớp trợ giúp giao diện thực hiện để sử dụng cho toán học Loại đã có trong Ma trận. – CodexArcanum

+0

Vì vậy, nếu tôi muốn lặp lại lớp 'Display' tôi có thể làm như sau:' val show: Type -> IDisplay' sau đó có 'let Showable = Dict ' và cuối cùng là triển khai giao diện IDisplay trên kiểu trình trợ giúp Foo của tôi, thêm kiểu Foo và helper vào từ điển, sau đó có phương thức 'show' tìm kiếm helper trong từ điển và gọi đúng phương thức? – CodexArcanum

+49

Ngoài ra, tôi thấy thật buồn cười khi lần đầu tiên tôi vào Haskell, tôi nghĩ rằng các lớp kiểu là Giao diện crappy với đóng gói không hợp lệ; và bây giờ tôi bắt đầu nghĩ rằng các Object là những lớp kiểu crappy với tính đa hình kém. "Họ" đã đúng khi tôi đọc rằng FP học tập sẽ làm hỏng bạn cho OOP. – CodexArcanum

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