Python vs Scheme: using files as both modules and programs
Python has a useful idiom, that allows one to use the same file as both a module and a program. Consider this simple example:
# foo.py
def bar(x):
print "bar says:", x
if __name__ == "__main__":
bar(42)
The if __name__ == “__main__” clause is only executed if foo.py is run as the “main program”. In other words, we can do both
$ python foo.py bar says: 42
and
$ python Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04) [GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import foo >>> foo.bar(33) bar says: 33
Although admittedly a bit of a hack, this construct is well-known and often used.
Now, on to Chicken Scheme. Can we do the same? It required a bit of poking around in documentation and mailing list, but it appears the answer is yes.
;; foo.scm (define (foo x) (print "foo says: " x)) (define (main args) (print "args: " args) (foo 42))
Now, we can run this as a script with csi -ss (which looks for a function called main and automagically calls it):
$ csi -ss foo.scm args: () foo says: 42
Notice that the main function has an argument args, which contains the command line arguments passed to the program:
$ csi -ss foo.scm 1 2 3 args: (1 2 3) foo says: 42
We can also import foo.scm from within an interactive session, in which cases main is not called:
$ csi CHICKEN Version 3.0.0 - macosx-unix-gnu-x86 [ manyargs dload ptables applyhook ] (c)2000-2008 Felix L. Winkelmann compiled 2008-03-05 on niflheim.local (Darwin) ; loading /Users/zephyrfalcon/.csirc ... ; loading /usr/local/lib/chicken/3/readline.so ... #;1> (use foo) ; loading ./foo.scm ... #;2> (foo 101) foo says: 101
But, but! Isn’t Chicken primarily a compiler? Does the above work too when using csc rather than csi? Actually it does, but the invocation is different. I use the following:
$ csc foo.scm -postlude "(main (cdr (argv)))" $ ./foo args: () foo says: 42 $ ./foo 1 2 3 args: (1 2 3) foo says: 42
The -postlude option can be used to specify code that runs when the executable is called. (Actually, the official explanation is: “Add EXPRESSIONS after all other toplevel expressions in the compiled file. This option may be given multiple times. Processing of this option takes place after processing of -epilogue.”)
I use (main (cdr (argv))) as the postlude expression, which seems to pass command line arguments the same way as csi -ss passes them to the main function (although there might be a catch that I’m not aware of). The cdr is necessary because the first item of the list returned by (argv) is the name of the calling program (e.g. “./foo”).
(If there’s a better way, please let me know, as my knowledge about the compiler is limited.)
Next up: parsing command line arguments in both Python and Scheme…
creidiki said,
March 23, 2008 @ 8:09 am
I’ll be interested in your take on commandline parsing in chicken.
Personally, I use the ‘args egg.
Hans Nowak said,
March 23, 2008 @ 9:35 am
That’s what I was going to talk about as well… ^_^’ Although both languages have several options.