Skip to main content
Commonmark migration
Source Link

#Gotos in Haskell

Gotos in Haskell

#Gotos in Haskell

Gotos in Haskell

Source Link
proud haskeller
  • 6.1k
  • 1
  • 23
  • 37

#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