Haskell is most closely related to the ML family of languages. This includes things like OCaml, of course, but also F# on the .NET platform. These languages share with Haskell the foundation of the type system and how data is used--algebraic data types, pattern matching, type inference, etc. They can differ substantially from Haskell on other points, of course--most MLs are strict and impure, to start with, and the popularity of Haskell as a vehicle for research in type systems and language design means that most ML-style languages tend to have less powerful (but potentially easier to understand) type systems. It's probably safe to say that while you may miss some things about Haskell, particularly at first, most Haskell programmers would probably feel comfortably at home in an ML pretty quickly, at a basic getting-things-done level. If you want a language with the same overall structure as Haskell, an ML is your best bet.
The functional side of Scala draws heavily from the ML tradition, as well, and also pulls in some advanced type system features familiar from Haskell, as well as a more standard OOP system integrated with the above. While OO in ML-style languages tends to be approached as more of a "model OO with basic functional tools" Scala lives and breathes Java-style OO. This has benefits for Java interop, as you might imagine, and presents more familiar working environment for OO programmers. However, coming from a Haskell background, you're more likely to be annoyed by ways that blending things together in Scala makes functional idioms clumsier, and find most Java APIs to be badly designed and needlessly difficult to use. So if you want something with a sophisticated type system that permits direct translations of many things in Haskell (with some extra syntactic overhead) and don't mind compromising on functional style, you might enjoy Scala.
Finally, while it may seem odd to consider, Clojure actually has a lot of things in common with Haskell at a more philosophical level. Most of what you'll find in Clojure's approach to state and values vs. identities is very close in spirit to what Haskell formalizes through the type system. Correspondingly, Clojure emphasizes Java interop to a smaller degree and doesn't worry as much about dragging in OOP, so in some ways Clojure's approach to functional programming itself may be closest to what you're already familiar with. I think it's telling in this regard that, to the best of my knowledge, Clojure is the only language besides Haskell that has an implementation of STM that's simple, effective, and just works. On the other hand, Clojure comes from the Lisp tradition and thus lacks the static type system and emphasis on algebraic data types and pattern matching found in ML-influenced languages. And of course it's a Lisp, which is itself a negative to some people (though I really don't know why).
Speaking for myself, with the disclaimer that my first experience with functional programming was in Scheme, I'd probably lean toward Clojure, with OCaml a likely second choice.
SDL is an excellent library with good Haskell bindings—and related libraries for images, audio, and text—for which enough tutorials exist to get you off the ground; being rather low-level, it requires a bit of care to keep things pretty and functional, but in my experience SDL and Haskell make a superb combination.
Best Answer
Unfortunately there are cases when one has to use
seq
in order to get a efficient/well working program for large data. So in many cases, you cannot do without it in production code. You can find more information in Real World Haskell, Chapter 25. Profiling and optimization.However, there are possibilities how to avoid using
seq
directly. This can make code cleaner and more robust. Some ideas:interact
. Lazy IO is known to have problems with managing resources (not just memory) and iteratees are designed exactly to overcome this. (I'd suggest to avoid lazy IO alltogether no matter how large your data is - see The problem with lazy I/O.)seq
directly use (or design your own) combinators such as foldl' or foldr' or strict versions of libraries (such as Data.Map.Strict or Control.Monad.State.Strict) that are designed for strict computations.seq
with strict pattern matching. Declaring strict constructor fields could be also useful in some cases.rseq
) or full NF (rdeepseq
) as well. There are many utility methods for working with collections, combining strategies etc.