19

Is it possible to turn off lazy evaluation in Haskell?

Is there a specific compiler flag of library to facilitate this?

I wanted to try something new with a old program that I had written a while back, to see if I can improve performance.

pyCthon
  • 11,746
  • 20
  • 73
  • 135
  • I expect the answer is no. You may want to read this to optimize or profile: http://book.realworldhaskell.org/read/profiling-and-optimization.html – James Black Mar 26 '13 at 01:43
  • 3
    It's part of the language pal, down at the deepest level. Is there something specific that you want to be eager? – bchurchill Mar 26 '13 at 01:46
  • @bchurchill it's more fueled by curiosity – pyCthon Mar 26 '13 at 01:49
  • @jozefg yeah but if i need/want to do it for one specific file/module i don't think i could do too much harm can I? – pyCthon Mar 26 '13 at 02:09
  • 3
    Is it possible to turn off strict evaluation in, say, C? I hope not, at least not without some serious redesign of the language semantics down to the deepest level. Of course, it is possible to implement some kind of lazy evaluation for specific expressions in C. All of those statements are true for Haskell, too, except with "strict" and "lazy" interchanged. – Yitz Mar 27 '13 at 13:56
  • 4
    @Yitz - real world computers use strict evaluation. Compilers don't need to work hard to evaluate strictly - they need to work hard (adding thunks, deferring evaluation, strictness analysis to determine where that's not necessary) to support laziness. It took GHCs authors a very long time to control those overheads and, despite that, a strict language is more referentially transparent than a lazy version of the same language - laziness means that time and space complexity aren't referentially transparent due to deferred computations and the mutable data structure used to keep track of them. –  Mar 30 '13 at 11:34
  • 2
    @Yitz - pervasive lazy evaluation is particularly a problem in interactive and real-time programs. Instead of doing work when there's plenty of time available, laziness defers that work until the result is needed - by definition, when it's already too late. There are workarounds in Haskell with selective strictness, of course, but that's not trivial - e.g. `$!` only forces the strict parameter to head-normal form - use it for a list and the members of that are still evaluated lazily. –  Mar 30 '13 at 11:40
  • 1
    @Steve314 - No, real computers use whatever semantics your compiler compiles them down to. At the hardware level, the semantics of modern processors are almost as far from the imperative model of C as they are from the non-strict model of Haskell. In any mature language, an experienced programmer can optimize down to whatever level is needed to match the hardware platform while still benefiting as much as possible from the strengths of the language, and Haskell is no exception. It's no harder in Haskell than it is in C. – Yitz Apr 28 '13 at 14:12
  • @Yitz - I can't believe your playing the non-strict-vs-lazy and sufficiently-smart-compiler cards - the only way to make sense of your `whatever semantics your compiler compiles them down to` claim. The fact is that GHC is state of the art and the GHC manual strongly recommends using explicit strictness because of the overheads from laziness that cannot be eliminated - e.g. "[Strict functions are your dear friends](http://www.haskell.org/ghc/docs/latest/html/users_guide/faster.html). –  Apr 28 '13 at 14:39
  • @Yitz - Of course you can force strictness explicitly in Haskell in those cases where the compilers strictness analysis doesn't handle that automatically, but my key point is that that *isn't* that simple. Another example - consider that if you write `seq x x` the result is *not* strict - the `seq` function itself is evaluated lazily. Even when you fix that, with pervasive laziness, if you force strictness at one particular point the computation tends to *still* get lazily deferred, just a step or two away. –  Apr 28 '13 at 14:44
  • 1
    @Yitz - in fact real world hardware isn't somewhere between lazy and strict - it's even more eager than eager, trying to evaluate things concurrently and ahead of time. And of course for simple computations, repeating a computation is often *much* faster than reading the precomputed result from memory - several arithmetic/logic instructions can run per cycle, whereas a single read of uncached main memory takes *hundreds* of cycles - making the whole premise of the single redex/memoization optimisation of outermost evaluation somewhat flawed. –  Apr 28 '13 at 14:53
  • @Yitz - BTW, semantics are what's defined irrespective of which compiler you use. The non-strict semantics of Haskell are part of the specification of Haskell - the compiler can only choose to use strict evaluation (or some other order) if the resulting semantics are equivalent to lazy evaluation. Of course calling Haskell lazy is sloppy, but claiming that the semantics is whatever the compiler does is just plain wrong. –  Apr 28 '13 at 15:16

7 Answers7

36

There are a number of ways to turn a lazy thing strict. You can:

  1. Explicitly insert a spurious pattern match.
  2. Use seq or its close relative ($!).
  3. Use BangPatterns.
  4. Use strictness annotations on your types.

More information here.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 4
    What is a spurious pattern match? – Nikita Volkov Mar 26 '13 at 08:10
  • 5
    @NikitaVolkov For example, for lists, replacing `foo` with `case xs of [] -> foo; _:_ -> foo`. – Daniel Wagner Mar 26 '13 at 11:32
  • 4
    Using `seq` or `$!` doesn't force a whole expression to be fully evaluated - it only forces "head normal form". For example, use `seq` on your list computation and it will eagerly evaluate the result list - but that's just which "cons" holds the head item of the list. The head member and the entire tail list may not have been evaluated yet. To force a complete expression to be fully evaluated, one option is `deepseq`, which means you need to instance the `NFData` typeclass. Another is a strictness-forcing function tailored for the result type. –  Mar 27 '13 at 11:16
  • the strict `Data.ByteString` is another – pyCthon Apr 07 '13 at 06:12
18

You can't turn off laziness, because the I/O system of Haskell depends on it. Without lazy evaluation this program would run into a busy loop without ever outputting anything:

main = forever (putStrLn "Hello!")

This is because forever c is an infinite program. With lazy evaluation the program is calculated only as far as necessary to run the next instruction. If you turn off laziness, every function becomes strict, including (>>), which basically makes the forever function diverge:

forever c = let cs = c >> cs in cs

However, you can add strictness annotations to constructors and patterns. When a function is strict, its argument is forced as part of the evaluation of the result independent of whether the argument is needed or not. This resembles eager evaluation.

ertes
  • 4,430
  • 1
  • 18
  • 23
  • 7
    This doesn't prove you can't turn off laziness - it only proves that the set of programs where you can turn off laziness and still have them work is heavily constrained. `forever` is just a particularly obvious problem function - there are many less obvious ones that would cause similar problems - but a sufficiently obsessive person might be able to identify and work with a Haskell subset that still works with laziness disabled. +1 anyway - that's still a good point. –  Mar 27 '13 at 11:23
  • 6
    You could define forever as `forever c = c >>= \_ -> forever c` which will work even with strict evaluation. – Twan van Laarhoven Mar 27 '13 at 14:14
11

In addition to what Daniel Wagner listed you may want to take a look at a similar question Is there a Haskell compiler or preprocessor that uses strict evaluation?.

  • Answers include the DDC compiler which attempts to make an strict version of haskell and only lazy explicitly
  • ghc plugin described in monad.reader 12
  • "Using nfdata and rnf everywhere" - solrize
  • and more

The predominate suggestion is to use profiling tools and learn how to optimize Haskell as it is however, since most would consider it a different language with non-strict evaluation turned off.

Community
  • 1
  • 1
Davorak
  • 7,362
  • 1
  • 38
  • 48
6

There's a variant of Haskell called pH (http://csg.csail.mit.edu/projects/languages/ph.shtml) which uses eager evaluation while still providing non-strict semantics. The Haskell Report is careful to say that it's a non-strict language. Laziness is the obvious way to describe and, apparently, to implement non-strictness.

So, if your question is "Can we use a different evaluation system while maintaining non-strict semantics", you could look at pH. If your question is "Is there a version of Haskell which shares the surface syntax but is strict by default", I think it's covered by other answers.

Hari
  • 170
  • 2
  • 7
5

You can enable the Strict pragma in a module, which will cause everything to be strict by default.

https://ghc.haskell.org/trac/ghc/wiki/StrictPragma

joelw
  • 401
  • 3
  • 11
3

The simple answer is no. The more complex answer is that the computational model upon which Haskell builds up and evaluates functions works in a lazy manner. As you will read in other answer there are ways to force evaluation of some functions earlier then normal, and it is occasionally adventitious to do so. But there is a large portion of valid Haskell which has no normal form. This includes the IO functions and a large amount of the standard prelude.

Conclusion: there is no more a way to turn of lazy evaluation in Haskell then there is a way to turn off pointer arithmetic in C or to turn off OO in Ruby of Java. I suspect that this is much farther then you though this question would take you. (There's no --strict mode), but if you really want to see just how deep the rabit hole goes, "Implementing Lazy Functional Languages on Stock Hardware: The Spineless Tagless G-machine" by Simon Peyton Jones is an adventure worth having.

John F. Miller
  • 26,961
  • 10
  • 71
  • 121
2

The strict-identity package has a strict version of the Identity monad.

You can find it here: https://hackage.haskell.org/package/strict-identity

The usage would look something like this:

foo = runStrictIdentity $! do
    x <- f a b
    y <- g x y
    return $! x + y

Each time return or bind >>= is used, the two parts are evaluated using seq, giving a reasonable guarantee of strictness provided your data structure isn't too deep. This works, for i.e. numbers and basic structures.

kvanbere
  • 3,289
  • 3
  • 27
  • 52