Search code examples
haskellcachingclosurespartial-application

Caching internals with a partially applied function


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.


Solution

  • 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