我如何将Haskell类型类转换为F#? [英] How would I translate a Haskell type class into F#?

查看:100
本文介绍了我如何将Haskell类型类转换为F#?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将哈斯克尔核心库的箭头翻译成F#(我认为这对于更好地理解箭头和F#是一个很好的练习,我可能能够在我正在开发的一个项目中使用它们)。但是,由于范例的不同,直接翻译是不可能的。 Haskell使用类型类来表达这些东西,但我不确定F#构造的最佳映射类型类的功能是否与F#的习惯用法类似。我有一些想法,但认为最好把它放在这里,看看什么被认为是功能最接近的。



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



对于那些接受我长时间解释的人:



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

  class类别cat其中
id :: cat aa
comp :: cat ab - > 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)

实例类别( - >)其中
id f = f
实例Arrow( - >)其中
arr f = f
first f = f *** id

尝试1:模块,简单类型,绑定



我的第一步就是直接使用Modules来组织映射,例如:

  type Arrow&''a,'b> =('a  - >'b)的箭头

let arr f = Arrow f
先让f = //执行第一个操作的一些代码

这很有效,但是它遗失了多态性,因为我没有实现类别,也不能轻松实现更专门化的箭头。

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

尝试1的问题是使用.fsi文件来定义方法(因此类型的执行更容易),并使用一些简单的类型调整来进行特殊化。

 键入ListArrow<'a,'b> =箭头< ['a],['b]> 
//或
类型ListArrow<'a,'b> = Arrow的LA< ['a],['b]>

但是fsi文件不能被重用(强制使用let bound函数的类型)其他实现,以及类型重命名/封装的东西是棘手的。



尝试2:对象模型和接口


合理化F#也被构建为OO,也许类型层次结构是这样做的正确方法。

 输入IArrow<'a,'b> = 
抽象成员组合:IArrow<'b,'c> - > IArrow<一个 c取代;
type Arrow<'a,'b>(func:'a->'b)=
interface IArrow<'a,'b>与
会员this.comp = //有趣的代码涉及箭头(有趣的x-> workOn x):> IArrow

除了获得静态方法(比如comp和其他操作符)像实例方法一样的痛苦外,还需要显式地上传结果。我也不确定这种方法仍然捕获了类型多态的完整表达。它也使得很难使用必须是静态方法的东西。



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



因此,另一个可能的改进是尽可能地声明接口为裸露,然后使用扩展方法为所有实现类型添加功能。

 输入IArrow<'a,'b> 
静态成员(&&& amp; amp;& amp; amp; amp;& amp; amp; b啊,但是这让我对所有类型的IArrow都使用一种方法。如果我想为ListArrows提供一个稍微不同的(&&& amp;& amp;& amp; amp;& amp; amp;&我还没有尝试过这种方法,但我想我可以对(&&& amp;& amp;& amp;& amp; 。



帮助我



那么我在这里应该做什么?我觉得OO应该足够强大以取代类型类,但我似乎无法弄清楚如何在F#中实现这一点。我的任何尝试都结束了吗?他们中的任何一个和它一样好并且必须足够好?

解决方案

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



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

  let inline i2(a:^ a,b:^ b)= 
((^ a或^ b):(静态成员实例:^ a(a,b))
let inline i3(a:^ a,b:^ b,c:^ c)=
((^ a或^ b或者^ c):(静态成员实例:^ a * ^ b * ^ c - > _)(a,b,c))

type T = T with
static member内联实例(a:'a)=
fun x - > i2(a,Unchecked.defaultof< r>)x:'r
静态成员内联实例(a:'a,b:'b)=
fun x - > i3(a,b,Unchecked.defaultof< r>)x:'r


类型Return =返回
静态成员实例(_Monad:Return,_:option< ;'a> = fun x - >一些x
静态成员实例(_Monad:Return,_:list<'a>)= fun x - > [x]
静态成员实例(_Monad:Return,_:'r - >'a)= fun x _ - > x
让内联返回'x = T.instance返回x

类型绑定=绑定
静态成员实例(_Monad:绑定,x:选项< _> ;, _:选项<'b>)= fun f - >
Option.bind f x
静态成员实例(_Monad:Bind,x:list< _&,_:list< b>)= fun f - >
List.collect f x
静态成员实例(_Monad:Bind,f:'r->'a,_:'r->'b)= fun k r - > k(fr)r
let inline(>> =)x(f:_->'R):'R = T.instance(Bind,x)f
let inline(> ; =>)fgx = fx>> = g

类型Kleisli<'a,'m> =('a - >'m)
的Kleisli让runKleisli(Kleisli f)= f

类型Id =具有
静态成员实例的Id(_Category:Id,_ :'r - >'r)= fun() - > id
静态成员内联实例(_Category:Id,_:Kleisli<'a,'b>)= fun() - >
Kleisli return'
let inline id'()= T.instance Id()

类型Comp =与
的静态成员实例(_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

类型Arr = Arr with
静态成员实例(_Arrow:Arr,_:_ - > _)= fun( f:_-> _) - > f
静态成员内联实例(_Arrow:Arr,_:Kleisli< _,_>)=
fun f - > Kleisli(返回'<<<< f>
let inline arr f = T.instance Arr f

type First =首先使用
静态成员实例(_Arrow:First ,f,_:'a - >'b)=
fun()(x,y) - > (f x,y)
静态成员内联实例(_Arrow:First,Kleisli f,_:Kleisli< _,_>)=
fun() - > Kleisli(fun(b,d) - > fb>> = fun c - > return'(c,d))
let inline first f = T.instance(First,f)()

让内联第二个f = let arr swap中的swap(x,y)=(y,x)>>>第一f>>> arr swap
let inline(***)f g = first f>>> (&&&& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;& amp;&& f *** g

用法:

 >令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)

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

注意:使用 id'()代替 id



更新:代码,否则这里是F#2.0版本< a>。



这里是对这种技术的详细解释,它是类型安全的,可扩展的,正如你可以看到的那样,即使对于某些高级类型类也是如此。


I'm trying to translate the Haskell core library's Arrows into F# (I think it's a good exercise to understanding Arrows and F# better, and I might be able to use them in a project I'm working on.) However, a direct translation isn't possible due to the difference in paradigms. Haskell uses type-classes to express this stuff, but I'm not sure what F# constructs best map the functionality of type-classes with the idioms of F#. I have a few thoughts, but figured it best to bring it up here and see what was considered to be the closest in functionality.

For the tl;dr crowd: How do I translate type-classes (a Haskell idiom) into F# idiomatic code?

For those accepting of my long explanation:

This code from the Haskell standard lib is an example of what I'm trying to translate:

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

Attempt 1: Modules, Simple Types, Let Bindings

My first shot at this was to simply map things over directly using Modules for organization, like:

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

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

That works, but it loses out on polymorphism, since I don't implement Categories and can't easily implement more specialized Arrows.

Attempt 1a: Refining using Signatures and types

One way to correct some issues with Attempt 1 is to use a .fsi file to define the methods (so the types enforce easier) and to use some simple type tweaks to specialize.

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

But the fsi file can't be reused (to enforce the types of the let bound functions) for other implementations, and the type renaming/encapsulating stuff is tricky.

Attempt 2: Object models and interfaces

Rationalizing that F# is built to be OO also, maybe a type hierarchy is the right way to do this.

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"

Aside from how much of a pain it can be to get what should be static methods (like comp and other operators) to act like instance methods, there's also the need to explicitly upcast the results. I'm also not sure that this methodology is still capturing the full expressiveness of type-class polymorphism. It also makes it hard to use things that MUST be static methods.

Attempt 2a: Refining using type extensions

So one more potential refinement is to declare the interfaces as bare as possible, then use extension methods to add functionality to all implementing types.

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

Ah, but this locks me into using one method for all types of IArrow. If I wanted a slightly different (&&&) for ListArrows, what can I do? I haven't tried this method yet, but I would guess I can shadow the (&&&), or at least provide a more specialized version, but I feel like I can't enforce the use of the correct variant.

Help me

So what am I supposed to do here? I feel like OO should be powerful enough to replace type-classes, but I can't seem to figure out how to make that happen in F#. Were any of my attempts close? Are any of them "as good as it gets" and that'll have to be good enough?

解决方案

Here's the approach I use to simulate Typeclasses (from http://code.google.com/p/fsharp-typeclasses/ ).

In your case, for Arrows could be something like this:

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

Usage:

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

Note: use id'() instead of id

Update: you need F# 3.0 to compile this code, otherwise here's the F# 2.0 version.

And here's a detailed explanation of this technique which is type-safe, extensible and as you can see works even with some Higher Kind Typeclasses.

这篇关于我如何将Haskell类型类转换为F#?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆