a quick shameless plug
first shaky prototype on its feet 2
mwotton@ubuntu-vm:/mnt/hgfs/projects/rhaskell/linux$ cat rubytest.rb require 'dl/import' module HaskyPants extend DL::Importable dlload "./libdynhs.so" extern "int fibonacci_hs(int)" end puts HaskyPants.fibonacci_hs(12) mwotton@ubuntu-vm:/mnt/hgfs/projects/rhaskell/linux$ ruby rubytest.rb 144
So, maybe this doesn't look so terribly clever, but this is a ruby process calling a Haskell function, more or less transparently. The Haskell function is built into a dynamic library using John Meacham's jhc compiler. jhc happily produces some C code that with some very minimal massaging (renaming of main to dontusethis_main) can be built with gcc into a dynamic library.
Ruby/DL can then load the library file into a ruby practice, and there we have it: a minimal, half-arsed, dodgy ruby-haskell bridge, as promised. I'll polish the code up a bit and chuck it on github tonight or tomorrow.
There's a lot left to do. It'd be nice to import this into RubyInline or something similar, so you can write Haskell code directly in your Ruby files. Also, currently you have to list the functions that you want to export from Haskell as well as the functions you want to import into Ruby, which is at least one too many times and possibly two: arguably, anything at the top level is intended to be imported, especially in an inline context. It'd also be great to have it work with compilers other than jhc: jhc is still a bit of a bear to install, and recompilation of big files is very slow. Still, this is a very encouraging spike.
Edit: it's now called Hubris, and you can get the extremely minimal code from http://github.com/mwotton/Hubris/tree/masterbouncing bloody but unbowed from the bowels of the beast
- GHC HEAD doesn't build on Mac OS X at all at the moment
- GHC HEAD won't build on Linux with --enable-shared - in an interesting failure mode on my machine, the build script actually loops. The mind boggles.
- GHC HEAD will build on Linux without --enable-shared. Puppies and rainbows, how lovely, but given that the whole purpose was to load libraries dynamically into a ruby interpreter, perhaps not as thrill-making as it could possibly have been.
minimal, dodgy, half-arsed ruby-haskell bridge
I'm currently contemplating a ruby-haskell bridge*, partly for the challenge, and partly because I think they go pretty well together**. Ruby has a shirtload of convenient frameworks and no shortage of people who like hacking in it, but it is, to be fair, horrifically slow at anything resembling computation. This is not much of a problem for your standard CRUDdy database-bound app, but doing anything like predictive in it would likely end in either tears or the heat-death of the universe. Haskell web frameworks, OTOH, are currently functional (if you'll pardon an awful, awful pun) but bare of all kinds of pleasantnesses that the ruby frameworks bring. In that spirit, then, why not let a thousand flowers bloom? Ruby for UI, Haskell for heavy lifting. ***
This reasoning sharply limits the requirements. We don't need full two-way communication: if Ruby's acting as front-end glue, it would be more than enough to let Ruby call Haskell. We don't need full mapping of data structures: functional analogues of object-oriented structures are a big, meaty, and in this case entirely unnecessary act of gold-plating. Hashes, arrays and primitive types a la JSON are plenty. Finally, we don't need re-entrancy, because once you're in Haskell-land, there's no (sanctioned) way to get back to ruby except by returning a value.
So, having limited our scope to something hopefully more than a toy but less than a thesis, how do we implement it? Two options spring immediately to mind:
- take our embedded haskell code, compile it down to a dynamic library, and link it in with the ruby interpreter the same way you'd embed a C lib. This is currently slightly awkward, because GHC likes to do the linking itself, in order to sprinkle in a page's worth of object files, but should not prove impossible.
- decorate the given code with a communication process like SOAP or JSON over pipes, and run it in a separate process. This is not really significantly different to running a persistent Haskell process that just accepts requests over an HTTP interface, except that the ruby process would be in control of keeping the Haskell process alive.
The second method is probably easier, but it's pretty clunky, and involves a bit more machinery behind the scenes. Ideally, I'd like something as smooth as Inline::C in Perl.
Have I missed something obvious? Flames, comments, experience reports, suggestions all welcome.
- * there's raskell, but it appears to be vapourware, apart from a warning about generating position independent code with GHC on platforms other than Mac OS X.
- ** and, fine, because I'm waiting for a call from the recruiter and I'm not very patient.
- *** snarky comments about speed of functional languages will be redirected to the computer language shootout. Even sans don's preternatural gift for optimisation, modern functional languages are fast.
