[Haskell-cafe] Why Haskell?
Matthew Bromberg
mattcbro at earthlink.net
Sat Jul 22 17:08:27 EDT 2006
I am currently in the middle of a reasonably large software simulation
of a wireless network that I'm programming
in Haskell and in C. Progress has been slower than anticipated and I
occasionally (probably because of looming deadlines)
ask myself did I take the right approach?
Here are the reasons why I chose Haskell initially
1) It's mathematical elegance, I thought would lend itself to the
complex mathematical expressions I am implementing.
2) It rates reasonably high in performance comparisons with other languages
http://shootout.alioth.debian.org/gp4sandbox/benchmark.php?test=all&lang=all
3) It has a very active community who are courteous and quite helpful
4) It has an interactive shell.
5) It integrates easily with C.
The idea would be to implement hooks to some fast matrix libraries and
some plotting routines in C and then develop the higher level logic in
Haskell.
Since marshalling arrays and such, is a bit of a pain I structured my C
matrix library in the form of stack based machine, using reference counting
to handle memory management and to minimize the amount of actual data I
need to pass between C and Haskell. This seems to work fairly well.
However I have been somewhat disabused of my original thought about how
development would proceed. I had hoped that I could get within a factor
of two or so of the fast prototyping, development times of a good
scripting language such as Matlab, Python or Lua, but I don't think I'm
achieving this unfortunately.
Here are some reasons why
1) Lack of debugging support. Yes there are print statements and trace,
but I would like to set a breakpoint. It would be nice to do so and
launch the GHCi interpreter with all the variable context supported. A
google search revealed that there is current work on this, but
unfortunately the package is in cabal, which has spotty support in
windows it seems.
2) Recompiling binaries (necessary in order to link in foreign object
code into GHCi) is slow using GHC. Moreover I have to restart GHCi if I
want to reload a changed DLL (unless there is a way to unload a DLL in
GHCi). It also requires jumping around between several console windows
to get the job done. (I'm not using an IDE does one exist?)
3) Lack of automatic type coercion for closely related types. In
particular I have to use CInt and CDouble to go into and out of C.
Unfortunately only a small subset of library functions will actually
support CInt so I have to explicitly coerce this values. I usually have
a host of compile errors initially because of this silly issue and it
makes the code ugly. What would help would be an automatic type
conversion from CInt to Int and CDouble to Double. Perhaps some warning
level could flag these if this were 'too dangerous'.
4) GHCi is really not as useful as I'd hoped. You can not just cut and
paste Haskell code from a text file and run it in the interpreter.
There is also this context issue concerning what modules are actually in
scope. So although in Haskell once I import a module, all of its
functions are in scope, in GHCi, if I load this module only the exported
functions from that module are in scope. The result, again, is that I
can not get an apples to apples idea of what is happening in my code.
Thus I have been using GHCi primarily as a syntax checker for Haskell
constructs.
5) Learning new programming patterns may have a role to play, but
sometimes things like iteration is cleaner than recursion. I worry
about performance issues however. I had to create a 50 Mbyte matrix and
I used what I thought, initially was an elegant contruction technique in
Haskell. Something like this
do
...
sequence $ [ reffill b s | s <- [0..(fi temits)-1], b <- [0..(fi nc)-1]]
...(push list on to matrix stack)
The refill function generates a matrix based on basestation index b and
SU index s and pushes it on the Matrix stack in C, inside an IO monad.
So it returns IO().
The list comprehension ends up doing a cartesian product of all (b,s)
pairs which in this case is a large number. fi is my shorthand for
doing an annoying CInt to Int conversion. The problem with this code is
that it takes about 500 Mbytes in memory instead of 50 Mb. Apparently
it loads up all the potential IO actions into the huge list before
executing it, taking the factor of 10 or so hit. There is probably a
nice space saving way of doing this, that executes each IO action before
generating the next one, but I was in too much of a hurry to figure it
out. This also makes me wonder about the efficiency of the C
interface. Each IO action calls a C function (using the unsafe
specifier in the import declaration). I hope there isn't too much
overhead with this. In my case I do almost no marshalling. Most calls
have no arguments and no returns, or at most require a handful of CInts
or CDoubles.
My application has 60-70% of it's statements in an IO monad calling
lots of C code. I begin to wonder if all those statements shouldn't in
fact just be in C to begin with. Thus I begin to wonder why I'm using
Haskell. Maybe I just need some encouragement that my choice will pay
off in the end.
More information about the Haskell-Cafe
mailing list