5 ms·
If you take a little care to e.g. make data types' fields strict, this tends to be a non-problem. It is harder to debug the code since it is lazily evaluated (a
by thirsteh 13y ago
If you take a little care to e.g. make data types' fields strict, this tends to be a non-problem. It is harder to debug the code since it is lazily evaluated (and it's not imperative!), but recent versions of GHC provide pretty powerful profiling, stack traces, etc.
I'm not sure that I would agree that you're flying "blind or dangerously" either way--at least not when it comes to building up large unevaluated thunks. I would agree somewhat if you were referring to code using unsafePerformIO, which makes it the programmer's responsibility not to break referential transparency. While that doesn't tend to be an issue either, Safe Haskell does mostly solve it.
- jes5199 13y agoI disagree. I've never seen a program fail because of an unsafePerformIO call, but I've never seen a Haskell project that didn't suffer from at least one mysterious, hard-to-solve unevaluated-thunks-filling-up-RAM bug. Even the company that was on HN recently cites thunk leaks as their biggest problem with Haskell: http://engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell/ http://engineering.imvu.com/2014/03/24/what-its-like-to-use-...
- implicit 13y agoHi. I wrote that article. I've never seen a Java application that hasn't succumbed to an unexpected NullPointerException at least once over its entire development cycle. That doesn't mean that the language isn't a reasonable choice. It's just a common problem that you have to accept on that platform. Space leaks in Haskell are similar. You're going to run into them every so often, and you'd really rather not, but they're easy enough to deal with.
- virtualwhys 13y agoI'd take a NullPointerException over a memory (edit: space) leak any day of the week; the former is instantly resolved, the latter, ?. Anyway, in Java 8 they've started taking steps to address the null problem with the new Optional type, and, FWIW, in Scala nulls are a more or less a non-issue when you use the FP side of the language.
- thirsteh 13y agoI think there may be some confusion about what a space leak in Haskell is. When you apply a function f to a, that is not actually evaluated. Rather, a "thunk" is created that will evaluate `f a` only when that value is actually needed. If, in your program, you never need anything, or don't "force" your function calls and data structures ("pretend" to need something) in intermediary stages, then the thunks may take up a non-trivial amount of memory. This is not a memory leak in the traditional sense, just temporarily increased memory usage. It is very easy to (pre-emptively) handle most space leaks in Haskell, but you do need to know how they arise. There are two very simply rules you can follow that take care of the vast majority of space leaks: 1. Make data fields strict unless you actually want them to be lazy, i.e. instead of: data Foo = Foo { bar :: String , baz :: Int } write data Foo = Foo { bar :: !String , baz :: !Int } 2. When you write recursive functions that depend on values which are not forced (e.g. pattern matched against) in each function call, use either `seq`/$! or bangpatterns to make sure the value is evaluated (to HNF) rather than building up excessive thunks. For example, instead of: acceptLoop :: Socket -> Int -> IO () acceptLoop sock connNum = do econn <- accept sock _ <- case econn of Left err -> printf "Error accepting connection %d: %s" connNum err Right conn -> forkIO $ runConn conn acceptLoop sock (connNum+1) write either acceptLoop :: Socket -> Int -> IO () acceptLoop sock !connNum = do econn <- accept sock _ <- case econn of Left err -> printf "Error accepting connection %d: %s" connNum err Right conn -> forkIO $ runConn conn acceptLoop sock (connNum+1) or acceptLoop :: Socket -> Int -> IO () acceptLoop sock connNum = do econn <- accept sock _ <- case econn of Left err -> printf "Error accepting connection %d: %s" connNum err Right conn -> forkIO $ runConn conn acceptLoop sock $! connNum+1 to make sure that connNum is always just a single value rather than a series of unevaluated thunks. That way you won't get a space leak if you rarely have problems accepting new connections.
- virtualwhys 13y agoThanks, helpful explanation. Now, that begs the question, why lazy by default and not opt-in lazy? From the outside looking in it seems that deep expertise is required in order to launch a Haskell production app with any degree of confidence (i.e. to quickly dig yourself out of runtime issues like space leaks where the means to avoid them may be known, but the means to resolve them when they occur, non-trivial).
- deleted 13y ago[deleted]
- deleted 13y ago[deleted]