我如何将Haskelltypes类转换为F#?

我试图将Haskell核心库的箭头翻译成F#(我认为这对于更好地理解箭头和F#是一个很好的练习,我可能可以在我正在进行的项目中使用它们)。但是,直接翻译由于范式的不同,是不可能的。 Haskell使用types类来expression这些东西,但是我不确定F#构造的最好用F#的习惯用法映射types类的function。 我有一些想法,但最好把它提出来,看看什么被认为是最接近的function。

对于人群:如何将types类(一个Haskell成语)翻译成F#惯用代码?

对于那些接受我长久解释的人来说:

Haskell标准库中的代码就是我试图翻译的一个例子:

class Category cat where id :: cat aa comp :: cat ab -> cat bc -> cat ac class Category a => Arrow a where arr :: (b -> c) -> abc first :: abc -> a (b,d) (c,d) instance Category (->) where id f = f instance Arrow (->) where arr f = f first f = f *** id 

尝试1:模块,简单types,让绑定

我的第一步是直接使用模块来组织映射,比如:

 type Arrow<'a,'b> = Arrow of ('a -> 'b) let arr f = Arrow f let first f = //some code that does the first op 

这是有效的,但它遗失多态性,因为我没有实现类别,不能轻易地实现更专业的箭头。

尝试1a:使用签名和types进行提炼

解决尝试1的一些问题的一种方法是使用.fsi文件来定义方法(所以types执行更容易),并使用一些简单的types调整来专门化。

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

但是对于其他的实现,fsi文件不能被重用(强制执行let bound函数的types),而且types重命名/封装的东西很棘手。

尝试2:对象模型和接口

合理化F#也被构build为面向对象,也许types层次结构是正确的方法。

 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" 

除了获得什么应该是静态方法(比如comp和其他操作符)像实例方法一样的痛苦之外,还需要显式地上传结果。 我也不确定这种方法是否仍然捕获了types多态的全部performance力。 这也使得很难使用必须是静态方法的东西。

尝试2a:使用types扩展进行改进

因此,还有一个潜在的改进是尽可能地将接口声明为裸露,然后使用扩展方法为所有实现types添加function。

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

啊,但是这使我对所有types的IArrow使用一种方法。 如果我想为ListArrows稍微不同(&&&),我该怎么办? 我还没有尝试过这个方法,但我想我可以隐藏(&&&),或者至less提供一个更专业化的版本,但我觉得我不能强制使用正确的变体。

帮我

那么我应该在这里做什么? 我觉得OO应该足够强大来取代types类,但我似乎无法弄清楚如何在F#中做到这一点。 我的任何尝试都是closures的吗? 他们中的任何一个都“尽善尽美”,而且必须足够好?

以下是我用来模拟Typeclasses的方法(来自http://code.google.com/p/fsharp-typeclasses/ )。

在你的情况下,对于箭头可能是这样的:

 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 fx static member instance (_Monad:Bind, x:list<_> , _:list<'b> ) = fun f -> List.collect fx static member instance (_Monad:Bind, f:'r->'a, _:'r->'b) = fun kr -> k (fr) r let inline (>>=) x (f:_->'R) : 'R = T.instance (Bind, x) f let inline (>=>) fgx = fx >>= 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 (<<<) fg = T.instance (Comp, f) g let inline (>>>) gf = 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) -> (fx, y) static member inline instance (_Arrow:First, Kleisli f, _:Kleisli<_,_>) = fun () -> Kleisli (fun (b,d) -> fb >>= 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 ( *** ) fg = first f >>> second g let inline ( &&& ) fg = arr (fun b -> (b,b)) >>> f *** g 

用法:

 > 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:f@4-14> > 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] 

注意:使用id'()而不是id

更新:你需要F#3.0来编译这个代码,否则这里是F#2.0版本 。

下面是对这种技术的详细解释,它是types安全的,可扩展的,甚至可以用一些更高级的types来看到。

我简短的回答是:

OO不够强大,无法replacetypes类。

最直接的翻译是通过一个操作字典,就像在一个典型的types类实现中一样。 这就是说,如果types类Foo定义了三个方法,那么定义一个名为Foo的类/loggingtypes,然后改变函数

 Foo a => yadda -> yadda -> yadda 

像function一样

 Foo -> yadda -> yadda -> yadda 

并且在每个呼叫站点,您都知道具体的“实例”要根据呼叫站点的types进行传递。

以下是我的意思的简短例子:

 // 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"] 

也可以看看

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