Search code examples
haskellcontinuationsfunction-compositioncontinuation-passing

Is there a way to chain functions like withCString?


Is there a way to chain functions like withCString? By that I mean any function that looks something like f :: Foo -> (CFoo -> IO a) -> IO a.

For example, lets say there is a function cFunc :: CString -> CFoo -> CBar -> IO ()

Usualy, I would do something like:

haskellFunc string foo bar =
  withCString string $ \ cString ->
    withCFoo foo $ \ cFoo ->
      withCBar bar $ \ cBar ->
        cFunc cString cFoo cBar

But i would like to do something like:

haskellFunc = (withCString |.| withCFoo |.| withCBar) cFunc

with some appropriate composition operator |.|.

I'm writing library with a lot of C bindings, and this boilerplate comes often. Am I doing something wrong?


Solution

  • You can use the Continuation applicative for composing these a -> (b -> IO c) -> IO c functions:

    import Control.Monad.Cont
    
    haskellFunc :: String -> Foo -> Bar -> IO ()
    haskellFunc string foo bar = flip runCont id $ 
        cFunc <$> 
          cont (withCString string) <*> 
          cont (withCFoo foo) <*> 
          cont (withCBar bar)
    

    Or with a bit of extra syntax:

    haskellFunc' :: String -> Foo -> Bar -> IO ()
    haskellFunc' string foo bar = flip runCont id $
        cFunc <<$>> withCString string <<*>> withCFoo foo <<*>> withCBar bar
      where
        f <<$>> x = f <$> cont x
        f <<*>> x = f <*> cont x