goto considered essential
- Posted by Tom Dailey <thomasdailey at comcast.net> Jan 21, 2006
- 545 views
Consider the following program fragment, which copies one text file into another text file: sequence cmd_args sequence cmd_line_words sequence input_file_name integer input_file_nr object line sequence output_file_name integer output_file_nr cmd_line_words = command_line ( ) if length ( cmd_line_words ) != 4 then Printf ( "Exactly two arguments expected -- %d supplied.\n", { length ( cmd_line_words ) - 2 } ) else cmd_args = cmd_line_words [ 3..4 ] input_file_name = cmd_args [ 1 ] input_file_nr = open ( input_file_name, "r" ) if input_file_nr = -1 then Printf ( "Can't open input file %s.\n", { input_file_name } ) else output_file_name = cmd_args [ 2 ] output_file_nr = open ( output_file_name, "w" ) if output_file_nr = -1 then Printf ( "Can't open output file %s.\n", { output_file_name } ) else while 1 do line = gets ( input_file_nr ) if atom ( line ) then exit end if puts ( output_file_nr, line ) end while Printf ( "%s copied to %s.\n", { input_file_name, output_file_name } ) end if end if end if Without a goto statement, or something like it, we have the creeping margin problem. In production code, with many error checks in the main logic, this can make code very hard to understand. With a goto, we have cmd_line_words = command_line ( ) if length ( cmd_line_words ) != 4 then goto arg_count_is_wrong end if cmd_args = cmd_line_words [ 3..4 ] input_file_name = cmd_args [ 1 ] input_file_nr = open ( input_file_name, "r" ) if input_file_nr = -1 then goto cant_open_input_file end if output_file_name = cmd_args [ 2 ] output_file_nr = open ( output_file_name, "w" ) if output_file_nr = -1 then goto cant_open_output_file end if while 1 do line = gets ( input_file_nr ) if atom ( line ) then exit end if puts ( output_file_nr, line ) end while Printf ( "%s copied to %s.\n", { input_file_name, output_file_name } ) goto halt arg_count_is_wrong: Printf ( "Exactly two arguments expected -- %d supplied.\n", { length ( cmd_line_words ) - 2 } ) goto halt cant_open_input_file: Printf ( "Can't open input file %s.\n", { input_file_name } ) goto halt cant_open_output_file: Printf ( "Can't open output file %s.\n", { output_file_name } ) goto halt halt: Here, the main (non-error) logic is much more apparent. But what, you ask, is then to prevent evil programmers from generating spaghetti code? Well, nothing. So perhaps a more constrained construct would be better. In the spirit of Ada exceptions, we might try something like this: -- -- Function/procedure body or main program starts here. -- cmd_line_words = command_line ( ) exception arg_count_is_wrong if length ( cmd_line_words ) != 4 cmd_args = cmd_line_words [ 3..4 ] input_file_name = cmd_args [ 1 ] input_file_nr = open ( input_file_name, "r" ) exception cant_open_input_file if input_file_nr = -1 output_file_name = cmd_args [ 2 ] output_file_nr = open ( output_file_name, "w" ) exception cant_open_output_file if output_file_nr = -1 while 1 do line = gets ( input_file_nr ) exit if atom ( line ) puts ( output_file_nr, line ) end while Printf ( "%s has been copied to %s.\n", { input_file_name, output_file_name } ) -- -- Normal function/procedure return or program termination occurs here. -- Exception handlers follow function/procedure body or main program. -- when arg_count_is_wrong do Printf ( "Exactly two arguments expected -- %d supplied.\n", { length ( cmd_line_words ) - 2 } ) -- implicit return or halt here when cant_open_input_file do Printf ( "Can't open input file %s.\n", { input_file_name } ) -- implicit return or halt here when cant_open_output_file do Printf ( "Can't open output file %s.\n", { output_file_name } ) -- implicit return or halt here -- -- Function/procedure body or main program source code ends here. -- The main logic is really obvious here. Not only have we eliminated the unconstrained goto, but we have reduced the number of lines per exception check from three to one, thus reducing the visual noise that these checks introduce into the main logic. (Also, note the Ada-like conditional exit statement.) For me, some such capability is necessary in any programming language that is to be used to write large production-quality programs. I make these points because I understand that a major new release of Euphoria might be in the works, and so this might be a good time to consider new features. What do you think? Tom Dailey