Created
June 15, 2023 16:52
-
-
Save rexim/f9115cf09b3467cd3581862fda58cc42 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"))] |
Something
@scouarn 🥇 (I don't know why GitHub doesn't allow reactions on Gist comments 🦜)
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 ()
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?
Hi there ! I don't understand why do you iterate for each function of blocks , you can simply just run the first one ?
let exec (blocks: (string * (unit -> unit)) list): unit =
match blocks with
| [] -> ()
| (_,foo) :: _ -> foo ()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, I was thinking of defining labels as mutually recursive functions. The downside is that
goto
calls have to be tail calls, hence thebegin/end
block for theelse
.My other idea was to define a binding operator to do the
try/with
but jumping forward doesn't work :