Skip to content

Instantly share code, notes, and snippets.

@ch3pjw
Created January 24, 2019 15:48
Show Gist options
  • Save ch3pjw/41bc429d8f9503ab6c343aad11279ffc to your computer and use it in GitHub Desktop.
Save ch3pjw/41bc429d8f9503ab6c343aad11279ffc to your computer and use it in GitHub Desktop.
Preventing return of a resource from a bracket
{-# LANGUAGE
RankNTypes
#-}
module Main where
data MyResource a = MyResource {unMyResource :: String}
withMyResource :: String -> (forall a. MyResource a -> IO r) -> IO r
withMyResource s f = f (MyResource s)
main :: IO ()
main = do
withMyResource "hello"
$ \res -> putStrLn $ unMyResource res
-- Does not compile:
-- sneakyRes <- withMyResource "world"
-- $ \res -> return res
-- putStrLn $ unMyResource sneakyRes
@ch3pjw
Copy link
Author

ch3pjw commented Jan 24, 2019

The trick is the rank 2 type of withMyResource that lets it dictate the type of the passed function. You should get the following warning if you try to compile the commented section:

Bracket.hs:19:15: error:
    • Couldn't match type ‘a0’ with ‘a’
        because type variable ‘a’ would escape its scope
      This (rigid, skolem) type variable is bound by
        a type expected by the context:
          MyResource a -> IO (MyResource a0)
        at Bracket.hs:(18,16)-(19,24)
      Expected type: IO (MyResource a0)
        Actual type: IO (MyResource a)
    • In the expression: return res
      In the second argument of ‘($)’, namely ‘\ res -> return res’
      In a stmt of a 'do' block:
        sneakyRes <- withMyResource "world" $ \ res -> return res
    • Relevant bindings include

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment