Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 244
CodexArcanum Avatar asked Oct 27 '10 15:10

CodexArcanum


People also ask

What is a Haskell type class?

Haskell classes are roughly similar to a Java interface. Like an interface declaration, a Haskell class declaration defines a protocol for using an object rather than defining an object itself. Haskell does not support the C++ overloading style in which functions with different types share a common name.

Is Haskell OOP?

Haskell isn't an object-oriented language. All of the functionality built here from scratch already exists in a much more powerful form, using Haskell's type system.

Does Haskell have inheritance?

Does Haskell have inheritance? Well, no, it doesn't, because Haskell does not have objects, and inheritance is a relationship between two objects. Objects are a combination of internal state (data) and methods (behavior).


1 Answers

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

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

like image 122
Brian Avatar answered Oct 05 '22 17:10

Brian