19

I often find this pattern in Haskell code:

options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar

...

doSomething :: Foo -> Bar
doSomething = unsafePerformIO $ do
  opt <- readMVar options
  doSomething' where ...

Basically, one has a record of options or something similar, that is initially set at the program's beginning. As the programmer is lazy, he doesn't want to carry the options record all over the program. He defines an MVar to keep it - defined by an ugly use of unsafePerformIO. The programmer ensures, that the state is set only once and before any operation has taken place. Now each part of the program has to use unsafePerformIO again, just to extract the options.

In my opinion, such a variable is considered pragmatically pure (don't beat me). Is there a library that abstracts this concept away and ensures that the variable is set only once, i.e. that no call is done before that initialization and that one doesn't have to write unsafeFireZeMissilesAndMakeYourCodeUglyAnd DisgustingBecauseOfThisLongFunctionName

crockeea
  • 21,651
  • 10
  • 48
  • 101
fuz
  • 88,405
  • 25
  • 200
  • 352
  • 1
    Much was said on the simpler issue of just IO initializers in an old [thread](http://www.haskell.org/pipermail/haskell/2004-November/014732.html). I don't think anything was ever happily solved. – Anthony May 20 '11 at 18:40
  • 4
    Is this the "configuration" problem? Oleg Kiselyov and Chung-chieh Shan had a functional pearl about it a few years ago called "Implicit Configurations" - http://www.cs.rutgers.edu/~ccshan/prepose/p1214-kiselyov.pdf – stephen tetley May 20 '11 at 18:59
  • @stephen tetley: That's useful. +1 – fuz May 20 '11 at 19:03
  • common use? citation needed.. – yairchu May 20 '11 at 21:04
  • @yairchu: I saw this pattern quite often. Some people described it as "the only way in Haskell to use global variables" – fuz May 20 '11 at 21:29
  • 4
    @yairchu: Yeah, I don't really buy "common use" either. `unsafePerformIO` in top-level bindings for certain limited kinds of initialization I can imagine, like creating an `IORef`. Top level bindings doing arbitrary `IO` wouldn't *surprise* me, but it seems unwise. Using `unsafePerformIO` to read bits of potentially mutable data from inside assorted functions is just ridiculous. – C. A. McCann May 20 '11 at 21:30
  • 2
    My philosophical response to this question: http://lukepalmer.wordpress.com/2011/05/20/the-whole-program-fallacy/ – luqui May 20 '11 at 23:12
  • 3
    +1 for a good question. I don't think that it's a good way to design things, but people wanting to do things like this is common enough to deserve some thoughtful responses. A SO question is as good a place as any to ask the dual questions of whether and how to do this and record the various responses. – mokus May 21 '11 at 11:54
  • A similar problem (a mutable global variable) is further discussed [on the Haskell wiki](http://www.haskell.org/haskellwiki/Top_level_mutable_state#The_problem). – Robin Green May 22 '11 at 14:18
  • @FUZxxl - I'm trying to clean up the [tag:pure] tag - [see meta](http://meta.stackexchange.com/questions/171779/what-is-the-pure-tag-for) for more info. I don't know anything about [tag:haskall], so can I ask for your advice on other tags to use on this question? Would [tag:purely-functional] or [tag:pure-function] or [tag:pure-procedure] work on this question? – Richard JP Le Guen Mar 15 '13 at 16:19
  • @RichardJPLeGuen [purely functional] is okay. I'm going to fix that up. – fuz Mar 16 '13 at 08:25

5 Answers5

21

Those who would trade essential referential transparency for a little temporary convenience deserve neither purity nor convenience.

This is a bad idea. The code that you're finding this in is bad code.*

There's no way to fully wrap this pattern up safely, because it is not a safe pattern. Do not do this in your code. Do not look for a safe way to do this. There is not a safe way to do this. Put the unsafePerformIO down on the floor, slowly, and back away from the console...

*There are legitimate reasons that people do use top level MVars, but those reasons have to do with bindings to foreign code for the most part, or a few other things where the alternative is very messy. In those instances, as far as I know, however, the top level MVars are not accessed from behind unsafePerformIO.

sclv
  • 38,665
  • 7
  • 99
  • 204
  • 7
    So JHC has bad code. GHC had a lot of that a long time ago, and they have slowly been converting to the pure way. It's the only sensible way. – augustss May 21 '11 at 00:28
  • Can you give concrete examples of the unsafety? – Ganesh Sittampalam May 21 '11 at 20:39
  • 2
    @Ganesh: You can't force that the initializer is run before the ref is accessed. If you thread through an record on the other hand (via implicit params or directly) then that can serve as a "proof obligation" of sorts. On top of that, there's the other usual problems with top level IORefs. – sclv May 24 '11 at 19:27
  • @sclv that's not true: the argument of `unsafePerformIO` is run the first time the result is evaluated, even if it's only evaluated to WHNF using `seq` – Jeremy List Apr 23 '15 at 02:07
10

If you are using MVar for holding settings or something similar, why don't you try reader monad?

foo :: ReaderT OptionRecord IO ()
foo = do
    options <- ask
    fireMissiles

main = runReaderT foo (OptionRecord "foo")

(And regular Reader if you don't require IO :P)

Masse
  • 4,334
  • 3
  • 30
  • 41
  • 2
    You miss the point. A pragmatic programmer wants to avoid putting an extra layer on each and every function. – fuz May 20 '11 at 18:41
  • 22
    @FUZxxl: Only functions that actually need the options would have an extra layer, and hiding the fact that some functions depend on outside data is not "pragmatic", it's sloppy. This is no different than tossing global variables around in an impure language. – C. A. McCann May 20 '11 at 18:48
  • @camccann: Let's consider I have a language setting for translatable error messages as state. I need them almost everywhere. I don't want to add an extra 10 kB of boilerplate, just to take this into account. – fuz May 20 '11 at 18:52
  • 4
    @FUZxxl: In order to do anything with the error messages, you'll have to be in `IO` at some point. So instead of using strings for errors, use `type TranslatableError = OptionRecord -> IO String`, then apply that to the options before displaying the messages. Just a few extra lines, not piles of boilerplate. – C. A. McCann May 20 '11 at 18:59
  • @camccann: It is the same reason why one uses the function `error`: Because one don't wants to carry the environment needed to throw an error. – fuz May 20 '11 at 19:00
  • 1
    @FUZxxl: Using `error` for anything other than "this can never happen" failures is *also* a terrible idea and indicates sloppy code. And I just explained how to produce errors without carrying the environment around. – C. A. McCann May 20 '11 at 19:04
  • So... you mean by throwing an exception? – fuz May 20 '11 at 19:05
  • 4
    @FUZxxl: Not to mention that if a function can plausibly encounter errors that force it to give up and let a caller's error handler deal with it, you should be in some sort of error monad anyway, in which case adding on a `ReaderT` is no big deal; this is why many applications define their own monad transformer stack. If you're using actual exceptions and catching them in `IO` then, again, there's no reason you can't get the options there instead of using `unsafePerformIO`. The problems you are trying to solve here don't actually exist. – C. A. McCann May 20 '11 at 19:14
  • 6
    The idea that there is information like this which must be accessible to every point in the program in fallacious. We have many tools available to factor out this dependency -- code which does not *essentially* depend on this data can be given a type which does not (to accurately reflect that), and code which does can be minimized in scope. Try to think of your program not as a great beast of tens of thousands of lines of specification, but instead definitions of vocabulary and no more than a few hundred lines of specification. – luqui May 20 '11 at 22:15
  • I had this nagging feeling that MVars are a bad idea unless for concurrency, but I'm getting from your comments that even Reader is a bad idea, even though I've seen lots of examples where it's been used, or recommended to be used exactly for these kind of read only variables. Was I mistaken? – Masse May 21 '11 at 08:21
  • @Masse: I don't think anyone's saying not to use a reader monad for these kinds of variables, because that's exactly what it's for. If anything, I believe @luqui is suggesting that if large sections of a program need access to a bunch of external context, the program as a whole is probably badly structured. Using a reader monad would be merely treating the symptom, then. – C. A. McCann May 21 '11 at 23:50
  • I've seen them all: global variables, singletons, dynamic parameters, thread locals... Whenever they're used to hide the actual data flow [the programmer being lazy], those idioms have disastrous consequences on the code. Yes, it can be a burden to add a parameter to many intermediary functions because you need it in a low-level function. However, it has the merit to clearly expose the data dependency (i.e. what input is actually required to perform the function calculation) while remaining pure and non-monadic. So, ReaderT is just another mechanism to hide the data (w/ potential abuse). – gawi Aug 27 '11 at 02:23
5

Use implicit parameters. They're slightly less heavyweight than making every function have Reader or ReaderT in its type. You do have to change the type signatures of your functions, but I think such a change can be scripted. (Would make a nice feature for a Haskell IDE.)

Robin Green
  • 32,079
  • 16
  • 104
  • 187
  • 1
    The idea of implicit parameters is quite nice. They solve the problem exactly: One can set parameters, but don't has to care, when they are initialized. – fuz May 20 '11 at 21:33
  • Can you include a link to an article on implicit parameters? That would allow people scanning these answers to quickly look at this method and see if it is correct for them. – Bryan May 21 '11 at 13:47
  • 3
    @Bryan: The [GHC User's Guide covers it](http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extensions.html#implicit-parameters), well enough to get the idea at least. – C. A. McCann May 21 '11 at 23:41
2

There is an important reason for not using this pattern. As far as I know, in

options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar

Haskell gives no guarantees that options will be evaluated only once. Since the result of option is a pure value, it can be memoized and reused, but it can also be recomputed for every call (i.e. inlined) and the meaning of the program must not change (contrary to your case).

If you still decide to use this pattern, be sure to add {-# NOINLINE options #-}, otherwise it might get inlined and your program will fail! (And by this we're getting out of the guarantees given by the language and the type system and relying solely on the implementation of a particular compiler.)

This topic has been widely discussed and possible solutions are nicely summarized on Haskell Wiki in Top level mutable state. Currently it's not possible to safely abstract this pattern without some additional compiler support.

Petr
  • 62,528
  • 13
  • 153
  • 317
2

I often find this pattern in Haskell code:

Read different code.

As the programmer is lazy, he doesn't want to carry the options record all over the program. He defines an MVar to keep it - defined by an ugly use of unsafePerformIO. The programmer ensures, that the state is set only once and before any operation has taken place. Now each part of the program has to use unsafePerformIO again, just to extract the options.

Sounds like literally exactly what the reader monad accomplishes, except that the reader monad does it in a safe way. Instead of accommodating your own laziness, just write actual good code.