#Gotos in Haskell
the basic idea is that gotos can be partially simulated using the last statement in do
-notations. for example:
main = do
loop:
print 3
goto loop
is equivalent to
main = do
loop
loop = do
print 3
loop
because execution will jump to the last statement, it is optimal to express gotos.
because the way it is done, gotos only jumps when they are at the do
block of a toplevel definition directly. it is actually "call x and ignore the rest of the lexically seen statements" rather than "all x and ignore the rest of the statements", like a real goto.
the biggest problem is that when there is no way to leave execution from the middle of an IO action - even return
doesn't; return
does nothing when it isn't the last statement.
this overcomes this by capturing the rest of the statements by another do
block.
goto loop
print 3
becomes
const loop $ do
print 3
the print 3
statement is captured by the do
block, so loop
becomes the last statement.
this transformation also supports variables being present at the scope of the actions.
this is done by remembering the variables which are in scope, and passing them into the actions. for example:
printer r = do
loop:
putStrLn r
goto loop
print "this isn't executed"
this simply translates into:
printer r = do
loop r
loop = do
putStrLn r
const (loop r) $ do
print "this isn't executed"
some notes:
also, a return undefined
statement is added to ensure the capturing do
block is not empty.
because sometimes there is type ambiguity in the capturing do
block, instead of const
we use asTypeOf
, which is the same as const
but requires both its parameters to have the same type.
the actual implementation (in javascript):
function makeGoto(code)
{
var vars = [] // the known variables
// add the arguments to the functions to scope
code.split('\n')[0].split('=')[0].split(' ').slice(1).forEach(function(varname){vars.push(varname)})
return code.replace(/([ \t]*)([a-zA-Z]+):|([ \t]*)goto[ \t]+([a-zA-Z]+)|[ \t]+([a-zA-Z]+)[ \t]*<-/gm, function match(match, labelSpaces, label, gotoSpaces, goto, x)
{
if (label != undefined)
return labelSpaces+label+" "+vars.join(' ')+"\n"+label+" "+vars.join(' ')+"=do ";
else if(goto != undefined)
return gotoSpaces+"asTypeOf("+goto+" "+vars.join(' ')+")$do\n"+gotoSpaces+"return undefined";
else
{
vars.push(x);
return match
}
})
}
an exampe:
main = do
putSrtLn "a"
goto label
putStrLn "b"
label:
putStrLn "c"
becomes:
main = do
putStrLn "a"
asTypeOf(label )$do
return undefined
putStrLn "b"
label
label =do
putStrLn "c"
output:
a
c