5

Imagine such a function:

bar :: Foo -> A -> B -> C -> IO ()

That function performs some IO stuff using a Foo and other values. The Foo value has to be passed to bar, and can be retrieved from IO via this:

foo :: X -> IO Foo

Now, A, B, C and X are all plain pure values. I’d rather like such a bar function:

bar :: X -> A -> B -> C -> IO ()

And Foo would be generated in the bar function using the X value. If I do that :

let f = bar myX

f :: A -> B -> C -> IO (). If I call that function several times, the X value remains the same because of partial application, but since it’s an IO effect, it will be generated each time. Is there a native, built-in-ghc way to perform some kind of caching so that the Foo value is generated once – for the generated closure? I guess it’s all boxing-related, but I never figured out how to do that without using dirty IORef, expanding the parameters of bar, which is ugly.

phaazon
  • 1,972
  • 15
  • 21

1 Answers1

12

What you are literally asking would break referential transparency, a big "no" in Haskell. So that leaves me with the question, should I show you the unsafeLaunchMissiles kind of method that does (sometimes, if you are lucky and optimizations don't break it) what you literally ask but is highly discouraged, or should I show the clean way that changes the types just a little? Let me try the latter.

If you make your bar have the following type instead:

bar :: X -> IO (A -> B -> C -> IO ())

Then you can use, in a do block:

f <- bar myX

Alternatively, if you think that misses the point of redefining bar to take an X, then instead keep your first type for bar and do

f <- bar =<< foo myX
Ørjan Johansen
  • 18,119
  • 3
  • 43
  • 53
  • 1
    Yeah, I know the `unsafeScheisse` functions, I don’t want that. Your solution makes me think of something I often use, like the function passed to `control`. I like it, it sounds promising. – phaazon Sep 04 '14 at 11:18