Dear Abraham,
Now comes the question of how to map a C++ class hierarchy into haskell. It seems natural to try to map C++ classes into haskell typeclasses; however, there are a few issues involved with that.
You will quickly run into problems when doing that. The most important problem is that class instances are implicitly exported (ie. have a global scope). When different C++ classes implement the same methods, you will get unresolvable name clashes in Haskell. Fortunately, there exists a simple solution to model *single* inheritance object models in Haskell. It works with using phantom types where we encode the inheritance tree in the phantom type variable. Here is a quick example. Suppose we have the following classes: class Window { void Show(); }; class Frame : public Window { void SetTitle( const char* title ); }; class Control : public Window { ... }; class Button : publice Control { void SetLabel( const char* label ); }; Every object in Haskell will be represented by a simple "Ptr a". The phantom type "a" will encode the inheritance relation, that is, for each class we will create a new "phantom" data type that represents its class (or its part of the virtual method table). type Window a = Ptr (CWindow a) data CWindow a = CWindow Note that we will never use the "CWindow" constructor and that it therefore called a phantom data type. The "CWindow" type represents the Window class by itself, the "a" type variable represents the classes that may extend it. type Frame a = Window (CFrame a) data CFrame a = CFrame type Control a = Window (CControl a) data CControl a = CControl type Button a = Control (CButton a) data CButton a = CButton Ok, now that we have encoded the objects and inheritance relationships, we can give the signatures of all the methods: frameCreate :: IO (Frame ()) buttonCreate :: IO (Button ()) windowShow :: Window a -> IO () frameSetTitle :: String -> Frame a -> IO () buttonSetTitle :: String -> Button a -> IO () The creation function return objects where the type parameter is instantiated to unit () -- indeed such function creates exactly a Frame or Button, but nothing more. In contrast, a function like "windowShow" has its type parameter uninstantiated as it can receive any kind of window (any object that derives from a window). This corresponds to the contra-variance rule. do b <- buttonCreate windowShow b We can see that the above code does indeed work, the buttonCreate returns a "Button ()" that can be passed whenever a "Window a" is expected: Button () == Control (CButton ()) == Window (CControl (CButton ())) == Window a Pheew, quite a story. I hope it helps. You can read more about phantom types and inheritance in: Domain Specific Embedded Compilers, Daan Leijen and Erik Meijer. 2nd USENIX Conference on Domain-Specific Languages (DSL), Austin, USA, October 1999. Calling Hell from Heaven and Heaven from Hell Sigbjorn Finne, Daan Leijen, Erik Meijer, and Simon Peyton-Jones. Proceedings of the International Conference on Functional Programming (ICFP), Paris, France, 1999. and a recent paper with novel uses of phantom types to encode generic type constraints: James Cheney and Ralf Hinze. Phantom types. (I think it is published in "the fun of programming" ?)
I'd like to make a haskell binding for a C++ library. Most
I have done this for a upcoming wxHaskell library: a binding to the wxWindows GUI library. Just as Alastair, I also think that it is better to create (or generate) a C interface to the C++ library first, ie. extern "C" { void object_foo( Object* self, int arg ) { self->foo(arg); } } Furthermore, you probably need to generate dynamic link libraries (or shared object files) to link with GHC as the runtime systems of C (=GHC) and C++ are incompatible on many platforms.
First - it seems natural to use template haskell to do the code generation. That way, the library could either write the
I would advise against this: it is quite experimental and you would lock yourself into a single implementation. (I have made that mistake with haskellDB and TREX myself though :-). Since you will generate code anyway, you probably have no need for template stuff. All the best, Daan.
A separate datatype would have to be made for each C++ class to allow it to actually be instantiated, which isn't too bad. However, to allow haskell instances of the typeclass to call the old behavior it seems that there'd have to be duplicate functions of the ones in the typeclass, i.e.
class A a where foo :: a -> IO ()
foo_cpp :: (A a) => a -> IO ()
That seems to be needed to allow haskell instances to call the old implementation in their definition, but it rubs me the wrong way. Can anyone suggest an alternate method, or suggest a different direction entirely?
Abe
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell