簡體   English   中英

我如何將Haskell類型類轉換為F#?

[英]How would I translate a Haskell type class into F#?

我正在嘗試將Haskell核心庫的Arrows轉換為F#(我認為這對於更好地理解Arrows和F#是一個很好的練習,我可能能夠在我正在開發的項目中使用它們。)但是,直接翻譯由於范式的不同,這是不可能的。 Haskell使用類型類來表達這些東西,但是我不確定F#構造最好用F#的習語映射類型類的功能。 我有一些想法,但最好把它提到這里,看看哪些被認為是最接近的功能。

對於tl; dr crowd:我如何將類型類(一個Haskell成語)翻譯成F#慣用代碼?

對於那些接受我長篇解釋的人:

來自Haskell標准庫的代碼是我正在嘗試翻譯的一個例子:

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

嘗試1:模塊,簡單類型,讓綁定

我的第一個鏡頭是直接使用模塊組織來映射事物,例如:

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

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

這是有效的,但它失去了多態性,因為我沒有實現類別,也不能輕易實現更專業的箭頭。

嘗試1a:使用簽名和類型進行精煉

糾正嘗試1的一些問題的一種方法是使用.fsi文件來定義方法(因此類型強制執行更容易)並使用一些簡單的類型調整來專門化。

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

但是fsi文件不能被重用(以強制執行let綁定函數的類型)用於其他實現,並且類型重命名/封裝的東西是棘手的。

嘗試2:對象模型和接口

合理化F#也構建為OO,也許類型層次結構是正確的方法。

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和其他運算符)用作實例方法之外,還有多少痛苦,還需要明確地重新生成結果。 我也不確定這種方法是否仍在捕捉類型多態的完整表現力。 它也使得很難使用必須是靜態方法的東西。

嘗試2a:使用類型擴展進行精煉

因此,另一個可能的改進是盡可能地聲明接口,然后使用擴展方法向所有實現類型添加功能。

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

啊,但這鎖定了我對所有類型的IArrow使用一種方法。 如果我想要ListArrows略有不同(&&&),我該怎么辦? 我還沒有嘗試過這種方法,但我猜我可以隱藏(&&&),或者至少提供一個更專業的版本,但我覺得我不能強制使用正確的變體。

幫我

那我該怎么辦呢? 我覺得OO應該足夠強大來替換類型類,但我似乎無法弄清楚如何在F#中實現這一點。 我的任何嘗試都關閉了嗎? 他們中的任何一個“都是如此好”而且必須足夠好嗎?

我的簡短回答是:

OO不足以替換類型類。

最簡單的轉換是傳遞操作字典,就像在一個典型的類型類實現中一樣。 也就是說,如果類型類Foo定義了三個方法,那么定義一個名為Foo的類/記錄類型,然后更改函數

Foo a => yadda -> yadda -> yadda

像這樣的功能

Foo -> yadda -> yadda -> yadda

並且在每個呼叫站點,您根據呼叫站點的類型了解要傳遞的具體“實例”。

這是我的意思的一個簡短例子:

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

這是我用來模擬類型類的方法(來自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  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

用法:

> 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版本

這里是對這種技術的詳細解釋,它是類型安全的,可擴展的,你可以看到甚至可以使用一些更高級的類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM