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 > [EMAIL PROTECTED] > http://www.haskell.org/mailman/listinfo/haskell > > _______________________________________________ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell