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

前端 未结 2 1023
臣服心动
臣服心动 2020-12-04 14:45

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

2条回答
  •  感情败类
    2020-12-04 15:01

    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 = Kleisli 
    
    > 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 = [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.

提交回复
热议问题