text: Alfonso Acosta wrote: > On 12/13/06, Udo Stenzel <u.stenzel at web.de> wrote: > >Finished! Look Ma, no existentials, no Typeable, no wrappers, even the > >types have become simple! > > I like the fact that type parameters are removed, which makes them > homegeneus and solves the problem of storing them in a list but as a > drawback the Runner type is less intuitive than the simple run > function. Actually the Runner type _is_ a simple (impure) function. The complication is just that you wrote your example in such a way that 'run' has to return a new 'run' function, and that gives it the type (a where a = IO a), which is not possible in Haskell without the use of a newtype. [...] > As I said the example I posted is quite simplified. Actually the real > descriptor (a naive translation from a C struct) is: > > > > --, > -- ()} > > As you can see, apart from the run function , a Descriptor has some > other data and other functions, which can be _optional_ ( see > deactivate, and activate) Okay, beautiful solution first, again splitting Discriptor in two and ignoring some fields being optional. Again, you have a factory and an actual object, and we will implement it exactly this way, for the moment ignoring the exports to C. I'm also leaving out "implementationData", because it's impossible to see what that's used for. data HdMaker = HdMaker { uniqueID :: LadspaIndex, label :: String, ... instantiate :: LadspaIndex -> Maybe Hd } data Hd = Hd { connectPort :: LadspaIndex -> Ptr LadspaData -> IO Hd , activate :: IO () , run :: LadspaIndex -> [(LadspaIndex, PortData)] -> ([(LadspaIndex, PortData)], Hd) , deactivate :: IO () , cleanup :: IO () } newHdMaker ... = HdMaker { ... , instantiate = newHandle } where newHandle = Hd { connectPort = , activate = , run = , deactivate = , cleanup = } You should be able to see how this is to be fleshed out. The important point is that the fields of Hd (which are set in newHandle) don't need to be passed a handle of sorts later, since that handle is already available, though of course I can't infer where it is supposed to come from. As usual, add newStablePtr/derefStablePtr as needed. I guess the connectPort function has to construct yet another Hd. Now you don't want to split the Descriptor record, because the C world already decided to pass an additional opaque handle type. That's no problem: you handle is simple the set of functions that take handles as parameters. data Descriptor = Descriptor { uniqueID :: LadspaIndex, ... instantiate :: LadspaIndex -> Maybe Hd, connectPort :: Hd -> LadspaIndex -> Ptr LadspaData -> IO Hd, activate :: IO () ... } newDescriptor ... = Descriptor { ... instantiate = newHandle, connectPort Hd = hd_connectPort Hd, activate Hd = hd_activate Hd, ... } where the fields of Hd have to be suitably renamed. > Those optional funcions cause a problem when splitting the type Udo > did: The C code must know which of those are optional when calling > chooseDescriptor. The easiest thing to do is to always put them in and stick (return ()) where you don't actually need an action. Putting a Maybe there is also possible, but then a wrapper function has to check whether there is actually a function to be called. It's not clear why you think that is less of a problem if you lump everything into a single record, the check has to be made anyway. > The only elegant solution which I could come up to solve the problem > is simply avoiding to split the Descriptor by using unsafeCoerce# > (which is not that elegant) to store the descriptors in a list. That's neither elegant nor a solution.  :