views:

222

answers:

1

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?

+7  A: 

My brief answer is:

OO is not powerful enough to replace type classes.

The most straightforward translation is to pass a dictionary of operations, as in one typical typeclass implementation. That is if typeclass Foo defines three methods, then define a class/record type named Foo, and then change functions of

Foo a => yadda -> yadda -> yadda

to functions like

Foo -> yadda -> yadda -> yadda

and at each call site you know the concrete 'instance' to pass based on the type at the call-site.

Here's a short example of what I mean:

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

See also

http://blog.matthewdoig.com/?p=112

Brian
Hmmm... I think I saw something similar in regards to the F# Matrix class where it has a dictionary of <Type, "Interfaces cast as objects"> that told the matrix which interface-implementing helper classes to use for the math on the Type that was in the Matrix.
CodexArcanum
So if I wanted to duplicate the `Display` type class I might do that as: `val show : Type -> IDisplay` then have a `let Showable = Dict<Type, obj>` and finally implement the IDisplay interface on my Foo helper type, add the Foo type and the helper to the dictionary, then have the `show` method look up the helper in the dictionary and call the right method?
CodexArcanum
Also, I find it funny that when I first got into Haskell, I thought type-classes were crappy Interfaces with bad encapsulation; and now I'm starting to think Objects are crappy type-classes with poor polymorphism. "They" were right when I read that learning FP will ruin you for OOP.
CodexArcanum
Ah, I had seen that blog post before but not given it a good read, time to correct that I suppose. Thanks for the link. Also here is the blog post I mentioned about Matrices: http://fdatamining.blogspot.com/2010/03/f-inumerics-interface-and-matrix-class.html He also talks about the INumerics interface. And yeah, your link has it right, Monads (and Arrows) are a lot less useful if you can't properly abstract the basic operations on them.
CodexArcanum
This technique is not too bad for a typeclass like `Show`. However, I haven't seen any good techniques for when higher kinds are needed (e.g. in the original question, `cat` is of kind `* => * => *`, which can't be cleanly represented in .NET).
kvb
I don't think this technique can work for higher-kinded types. .NET type system does not support higher-kinded polymorphism like Haskell.
snk_kid
No, the method Brian presents will break down rather quickly when you start adding types that are themselves generic (higher-kinded). It seems the only recourse is the dictionary lookup method if type-class polymorphism is needed, or just manually writing out everything with lots of duplication to get all the functionality wired up.
CodexArcanum