continuation passing style - Haskell

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Vincent L

Hi,

I have a code that I'd like to port to CPS ; I'd like to have an "early return" in my code : the function looks for a file, and if it's not present, it returns an empty list, otherwise it parses the content of the file and return a list of strings coming from some regex matching in this file.

The current code is

 getRenduTxtNums :: FilePath -> IO [String]
 getRenduTxtNums path = do
         let rendu_txt = path </> "rendu.txt"
         fileExists <- testfile rendu_txt
         if not fileExists
           then return []
           else get_nums_from_file rendu_txt
   where
     get_nums_from_file rendu_txt = do
             content <- unpack <$> readTextFile rendu_txt -- readFile rendu_txt
             let res = getAllTextMatches $ content =~ (unpack "([[:digit:]])+") :: [String]
             return res

I started by writing the "root function" which run the continuation :

getRenduTxtNums :: FilePath -> IO [String]
getRenduTxtNums path = runCont (testing path) id

which means I need a testing :: FilePath -> Cont r (IO [String])function. I tried the following :

testing path = callCC $ \earlyret -> do
  let rendu_txt = path </> "rendu.txt"
  return $ do
    fileExists <- testfile rendu_txt
    when (not fileExists) $ earlyret []
    undefined

but it doesn't work. The funny thing is, if I remove when (not fileExists) $ earlyret [], it works so it looks a lot like I have issue by stacking monads.

How can I do what I have in mind ? Do I miss some liftcall at some place ?

Vincent L

(by the way is there a way to put "placeholder function" to get type hint from Haskell ? like undefined, but GHC would tell me "please use something of type X instead of undefined")

Torsten Schmits

Vincent L said:

(by the way is there a way to put "placeholder function" to get type hint from Haskell ? like undefined, but GHC would tell me "please use something of type X instead of undefined")

a typed hole is an underscore followed by an optional name, i.e. _ or _value, and will result in a compiler message

Vincent L

by the way I don't understand something

Vincent L

ContT r IO [String]is not the same as ContT r (IO [String]) ?

Vincent L

ho I confused Contand ContT

Vincent L

So for the record here is the version that works :

getRenduTxtNums :: FilePath -> IO [String]
getRenduTxtNums path = runContT (testing path) return

testing :: FilePath -> ContT r IO [String]
testing path = callCC $ \earlyret -> do
  let rendu_txt = path </> "rendu.txt"
  fileExists <- lift $ testfile @IO rendu_txt
  unless fileExists $ earlyret []
  content <- lift $ unpack <$> readTextFile rendu_txt
  let res = getAllTextMatches $ content =~ (unpack "([[:digit:]])+") :: [String]
  return res

The issue was that I need to use runContT and not just runCont

Torsten Schmits

congrats! I'm getting pretty curious now about what a Rendu is :sweat_smile:

Vincent L

It's a "deliverable"

Vincent L

(I'm mixing english and french, I wrote a tool in Haskell which uncompress some archives, run some script on their content and report the result. I was a beginner so it was quite awkward, I'm spending some time to write it in a more Haskellish way