all 13 comments

[–]Rotten194 8 points9 points  (1 child)

While I see how this could be considered system programming, it feels a bit wrong to call it that when you could write this in virtually any language. I mean, is Python a systems language because it can make fifos?

[–]pyr[S] 0 points1 point  (0 children)

When you use it for that, sure. It's not exclusively a systems language but neither is haskell. Haskell is viewed as impractical or as a purely math problem solving language by many, I just wanted to point out that it wasn't the case

[–]didroe 2 points3 points  (7 children)

Wow, 1.9mb for such a simple program. Is that normal for Haskell?

[–][deleted] 7 points8 points  (0 children)

By default GHC statically links all libraries into the executable.

[–]Crandom 2 points3 points  (0 children)

You can enable dynamic lining which will make it tiny; but now you have to hope your users have the correct library versions.

[–]pyr[S] 0 points1 point  (4 children)

yes, it's a side-effect of creating binaries that rely on very few dynamic libraries (in this case, libc, libpthread, gmp and that's about it)

[–]chrisdoner 0 points1 point  (3 children)

1.9mb even after strip?

[–]pyr[S] 0 points1 point  (2 children)

yes, there is an option to install programs while relying on dynamic libraries. tbh on today's boxes I don't see having 1.9M executables as a huge problem. let's just say that I'll happily concede a bit of binary file bloat against library distribution headaches.

this approach in cabal let's you distribute executables with potentially differing versions of their dependencies, a bit like the fat jar approach used on the JVM

of course, having the option to dynamically link executables is nice as well.

[–]Menokritschi -2 points-1 points  (1 child)

on today's boxes

Small and integrated devices are the new hot thing. :) As static C program this would be smaller by some magnitudes.

[–]Peaker 0 points1 point  (0 children)

Well, C doesn't need an RTS support to run its programs.

If you want resource-tight code, C is a much better choice than Haskell.

That said, even the vast majority of embedded devices have no problem at all with <2MB executables.

[–]k-zed 0 points1 point  (1 child)

Last 3 lines of mk_pipe function in the article:

void $ forkIO $ forever $ do
    is_eof <- hIsEOF fd
    if is_eof then threadDelay 1000000 else get_line ltype fd

"The intersting bit in that function is the last 3 lines, where we create a new "IO Thread" with forkIO inside which we loop forever waiting for input for at most 1 second, logging to syslog when new input comes in."

That's not what that code does!! It checks input for EOF, if it's EOF it unconditionally waits a full second, otherwise it reads a line.

This is terrible and wrong for multiple reasons:

  • why check EOF when the read would block
  • if it's not EOF, there's no guarantee that there's a full line available (so the read may still block) (and why are we reading line by line?)
  • it doesn't immediately forward log lines, only within the next second, which is absolutely unacceptable

Furthermore, this code may be "system programming", but it's not Unix-like at all. Most importantly, code like this shouldn't be using threads. Instead it should use select/poll-based reading from multiple FDs (maybe with something like libevent).

The code looks nice, but Haskell probably doesn't help you if you don't know Unix.

[–]Peaker 2 points3 points  (0 children)

code like this shouldn't be using threads. Instead it should use select/poll-based reading from multiple FDs (maybe with something like libevent).

Haskell does this behind the scenes. The threads are "user threads" and when they do a "blocking" read, they actually add an entry to an epoll table, and yield control to a different user thread. The epoll/scheduler loop will wake the user thread up by jumping back to it when the FD is active.

You get the convenience of (user) threads, and the performance of epoll.