Skip to content

Instantly share code, notes, and snippets.

@rexim
Created June 15, 2023 16:52
Show Gist options
  • Save rexim/f9115cf09b3467cd3581862fda58cc42 to your computer and use it in GitHub Desktop.
Save rexim/f9115cf09b3467cd3581862fda58cc42 to your computer and use it in GitHub Desktop.
open Printf
exception Goto of string
let label (name: string) = ()
let goto (name: string) = raise (Goto name)
let goto_block (blocks: (string * (unit -> unit)) list): unit =
let rec goto_block_impl (name: string option): unit =
try
let exec (blocks: (string * (unit -> unit)) list): unit =
blocks |> List.iter (fun (_, block) -> block ())
in
let rec skip (blocks: (string * (unit -> unit)) list) (entry: string) =
match blocks with
| [] -> ()
| (name, _) :: rest ->
if String.equal name entry
then exec blocks
else skip rest entry
in
match name with
| None -> exec blocks
| (Some entry) -> skip blocks entry
with
Goto name -> goto_block_impl (Some name)
in goto_block_impl None
let () =
let i = ref 0 in
goto_block
[("loop", (fun () ->
if !i >= 10 then goto "out" else ();
printf "%d: Hello, World\n" !i;
i := !i + 1;
goto "loop"));
("out", (fun () ->
printf "Done!\n"))]
@Blugatroff
Copy link

I tried porting this to haskell and, as you would expect, it worked just fine.

{-# LANGUAGE ScopedTypeVariables #-}

module Main where

import Control.Exception (Exception, throw, try)
import Control.Monad (when)
import Data.Foldable (find, for_)
import Data.IORef (newIORef, readIORef, modifyIORef)

main :: IO ()
main = do
  iRef <- newIORef 0
  runGoTo 
    [ ("loop", do
        i <- readIORef iRef
        when (i >= 10) $ goto "done"
        print i
        modifyIORef iRef (+ 1)
        goto "loop")
    , ("done", putStrLn "DONE")]

newtype GoTo = GoTo String deriving (Show)
instance Exception GoTo

goto :: String -> IO ()
goto label = throw (GoTo label)

runGoTo :: [(String, IO ())] -> IO ()
runGoTo blocks = do
  result :: Either GoTo () <- try $ for_ blocks snd
  case result of
    Left (GoTo label) -> case find ((== label) . fst) blocks of
      Just (_, block) -> runGoTo (dropWhile ((/= label) . fst) blocks)
      Nothing -> error $ "Label not found: " <> label
    Right () -> return ()

@jpmassari
Copy link

jpmassari commented Dec 6, 2023

Guys, I tested a for loop of an object inside a block, and a normal function with a for loop. Any idea why the loop inside the goto_block mechanism always much faster?

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