Do stack overflow errors occur in Haskell?

…衆ロ難τιáo~ 提交于 2019-12-07 05:30:22

问题


As a pure functional programming language, Haskell intensively uses recursion. Do stack overflow errors occur in Haskell, like in Java? Why, or why not?


回答1:


Haskell uses the stack differently from Java, due to laziness.

In Java, a stack frame is created when a method is called, and freed when the method returns. So if f() is a recursive method, each recursive call to f() generates a stack frame, and these frames are strictly nested. You can get a stack overflow when you have a deep chain of recursive calls, like f() -> f() -> f() -> ….

Whereas in Haskell, a thunk is created when a function is called. A stack frame is created when a thunk is forced using pattern-matching (e.g. case), and freed when evaluation of the thunk is complete enough to return a value (which may contain more unevaluated thunks).

So if f is a recursive function, each call to f generates a thunk, and case on the result of this generates a stack frame, but these frames are only nested when there’s a dependency between thunks. And in fact, that’s what the seq primitive does: a `seq` b means “evaluate a before b, returning b”, but you can also think of it as adding a dependency of b on a, so when b is evaluated, a is also forced.

You can get a stack overflow when you have a deep chain of thunks to evaluate, for example in the excessively lazy foldl function:

foldl (+) 0 [1..5]
==
foldl (+) 0 (1 : 2 : 3 : 4 : 5 : [])
==
((((0 + 1) + 2) + 3) + 4) + 5

This generates a chain of thunks like so:

((+)
    ((+)
        ((+)
            ((+)
                ((+)
                    0
                    1)
                2)
            3)
        4)
    5)

When we force the result (for example, by printing it) we need to descend all the way down this chain in order to be able to start evaluating it, at the (+) 0 1 thunk.

So foldl often produces stack overflows for large inputs, which is why most of the time you should use foldl' (which is strict) when you want a left-associative fold. Instead of building a chain of nested thunks, foldl' evaluates the intermediate results immediately (0+1 = 1, 1+2 = 3, 3+3 = 6, …).




回答2:


Stack overflows won't occur in Haskell like they would in Java, etc., because evaluation and function calls happen differently.

In Java and C and other similar languages, function calls are implemented using a call stack. When you call a function, all of the function's local variables are allocated onto a call stack, and when the function finishes, the function is popped back off of the stack. Too many nested calls, and the call stack will overflow.

In Haskell, that's not necessarily how function calls work. In most compilers, like GHC, Haskell function calls are not implemented using call stacks. They are implemented using a completely different process, using allocation of thunks on the heap.

So, most haskell implementations don't implement a call stack for function calls in the first place, so the idea of a stack overflow is non-sensical. It'd be like talking about bath tubs overflowing in a locker room with only showers.

(GHC does use a call stack for thunk evaluations, but not for function calls. So stack overflows that might happen are of a completely different and unrelated nature than stack overflows from Java, C, etc.)



来源:https://stackoverflow.com/questions/43265198/do-stack-overflow-errors-occur-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!