|ePolyglot: Examination and development of multilanguage programming using Eiffel, Python, and Haskell|
|Prev||Chapter 13. Haskell from Eiffel||Next|
This is rather disappointing, and occupied a number of days of development time. The problem is that HaskellDirect, at least version 0.16, is really designed to expose C functions to Haskell rather than the other way around. It handles marshaling of primitive atomic values with panache, but generates this sort of code for marshaling the array of doubles:
Example 13-5. What HaskellDirect created for marshaling arrays in the beta_0 function (line numbers added for clarity)
1 beta_0_proxy :: [Prelude.Double] 2 -> [Prelude.Double] 3 -> Prelude.IO Prelude.Double 4 beta_0_proxy = wrap_beta_0 Regression.beta_0 5 6 foreign export "beta_0" beta_0_proxy :: Addr.Addr -> Prelude.Int -> Addr.Addr -> Prelude.Int -> Prelude.IO Prelude.Double 7 wrap_beta_0 :: ([Prelude.Double] -> [Prelude.Double] -> Prelude.Double) 8 -> Addr.Addr 9 -> Prelude.Int 10 -> Addr.Addr 11 -> Prelude.Int 12 -> Prelude.IO Prelude.Double 13 wrap_beta_0 beta_0_meth xs len_xs ys len_ys = 14 case beta_0_meth xs 15 ys of 16 o_beta_0 -> let 17 len_xs = Prelude.id (Prelude.length xs) 18 in 19 do 20 xs <- HDirect.marshalllist HDirect.sizeofDouble HDirect.writeDouble xs 21 let len_ys = Prelude.id (Prelude.length ys) 22 ys <- HDirect.marshalllist HDirect.sizeofDouble HDirect.writeDouble ys 23 Prelude.return (o_beta_0)
To those unfamiliar with Haskell (such as the ePolyglot developers), the above resembles utter gibberish. It took some time (and careful studying of the Haskell compiler errors) to understand what was going on.
Look at line 6 in the above source code. What that line says is that GHC should export the function beta_0_proxy under the name "beta_0", taking arguments of (pointer, int, pointer, int), and returning a double. This makes sense so far, since we do indeed want a C-available function taking pointers and integers (the addresses of our arrays, and their length) and returning a double as a result.
But a quick look at the beta_0_proxy function shows a mismatch--its type signature takes two lists of doubles and returns a double, which is close--but not close enough for a typesafe compiler. It's easy enough to (by hand) change the type signature of beta_0_proxy to match, by editing the lines around lines 1-2 to match the type signature of the foreign export in line 6.
However, that's not the whole story. A look at lines 14-23 above shows some other peculiarities--notably, that HaskellDirect is valiantly trying to do the opposite of what we want--it's trying to marshal a list into an array of doubles, rather than unmarshaling an array of doubles into a Haskell list!
Some poring through the HaskellDirect library to reveal the correct functions (unmarshallist instead of marshallist, readDouble instead of writeDouble, etc.) and some careful hand-correction of the generated code results in the following:
Example 13-6. What we wanted for marshaling arrays in the beta_0 function
beta_0_proxy :: Addr.Addr -> Prelude.Int -> Addr.Addr -> Prelude.Int -> Prelude.IO Prelude.Double beta_0_proxy = wrap_beta_0 Regression.beta_0 foreign export "beta_0" beta_0_proxy :: Addr.Addr -> Prelude.Int -> Addr.Addr -> Prelude.Int -> Prelude.IO Prelude.Double wrap_beta_0 :: ([Prelude.Double] -> [Prelude.Double] -> Prelude.Double) -> Addr.Addr -> Prelude.Int -> Addr.Addr -> Prelude.Int -> Prelude.IO Prelude.Double wrap_beta_0 beta_0_meth xs len_xs ys len_ys = do xs <- HDirect.unmarshalllist HDirect.sizeofDouble 0 (Word.intToWord32 len_xs) HDirect.readDouble xs ys <- HDirect.unmarshalllist HDirect.sizeofDouble 0 (Word.intToWord32 len_ys) HDirect.readDouble ys case beta_0_meth xs ys of o_beta_0 -> Prelude.return( o_beta_0 )
The code compiles. A quick test with a C stub program shows that the exported functions are indeed available from C. The HaskellDirect library is up to the challenge of exporting the functions, but the automatically-generated code from ihc requires laborious by-hand corrections to work properly, meaning that while this process can be made to work, it is not ready for production yet.