Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Indeed. And that's the major shortcoming I see that could prevent me from using Haskell in a production environment.

The guys who wrote XMonad got around it, so it is possible. You just need to be careful, and use enough strictness annotations.



Laziness is one of those features I can never make my mind up about. It is sometimes very convenient, but most of the time I never think about the fact that the semantics mean evaluation is lazy. Sometimes this negligence has bad consequences, and then one must invoke a carefully honed skill to correct the issue. Definitely doable, but it certainly feels like fighting the language (to me, anyway).

One of the craziest things is that Haskell has decided that type families should also be lazily evaluated. So you can hop into the interactive interpreter to inspect some types and get not-so-useful information, like

    somevar :: SomeTypeFunction (SomeTypeFunction2 Int) (SomeTypeFunction3 (SomeTypeFunction 3 (SomeTypeFunction3 String)))
which might actually evaluate to something simple like (String, Int), though you wouldn't know it without either tracing through the functions (yuck!) or writing some silly helper functions whose sole job is to force evaluation of the type functions (yuck!). I've never personally been thankful for this behavior.


Interesting. I'll try to reproduce your example. Could you make something up that I can just copy-and-paste into ghci? Thanks!

Chris Okasaki argues in "Purely Functional Data Structures" for making lazy evaluation optional, but easy to use. Some data structures have optimal asymptotics only when evaluated strictly and some only when evalutated lazily.

With better strictness analysers most of the problems lazyness is causing at the moment, may go away. A Haskell compiler is free to make your program (or parts of it) strict, if it can prove that this preserves semantics.


Here's some code:

  {-# LANGUAGE
          TypeFamilies,
          MultiParamTypeClasses,
          FlexibleInstances #-}

  data True = T
  data False = F

  class LogicOr a b where
    type Or a b
    typeOr :: a -> b -> Or a b
  instance LogicOr True b where
    type Or True b = True
    typeOr T b = T
  instance LogicOr a True where
    type Or a True = True
    typeOr b T = T
  instance LogicOr False False where
    type Or False False = False
    typeOr F F = F

  class Decide tf a b where
    type If tf a b
    nonFunctionalIf :: tf -> a -> b -> If tf a b
  instance Decide True a b where
    type If True a b = a
    nonFunctionalIf T a b = a
  instance Decide False a b where
    type If False a b = b
    nonFunctionalIf F a b = b

  whatIsMyType tf1 tf2 a b = nonFunctionalIf (typeOr tf1 tf2) a b
If we load this into ghci, we have the following example:

  *Main> :t whatIsMyType F F 2 "foo"
  whatIsMyType F F 2 "foo" :: (Num t) => If (Or False False) t [Char]
  *Main> :t whatIsMyType F F 2 "foo" :: String
  whatIsMyType F F 2 "foo" :: String :: String
In the first :t, ghci lazily (though correctly) gives us the type of whatIsMyType. In the second example, where we provide an explicit type annotation, ghci is able to (correctly) conclude that our annotation was correct by evaluating the various type functions it had given us before.


Thanks. I can reproduce your example.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: