On Apr 17, 2009, at 5:21 PM, Tom Faulhaber wrote:

> I'd also like to see a little more focus on the perl/python/ruby
> equivalence from a newbie perspective. That is, more clarity around
> startup, script execution (including having an equivalent to python's
> "if  __name__ == '__main__':" construct), class path management, etc.
> I know that this is one area where being in the JVM ecosystem makes
> our life worse rather than better, but approaching Clojure is still a
> bit daunting compared to these other languages.

I have a proposal for a standard way to make a namespace "executable"  
and to invoke it as a program/script.

The basic idea is to mark each namespace intended to be run as  
"program" such that its entry point can be found and to provide  
support for calling that entry point easily.

An outline:

     - add a key to the "ns" form to specify, as a symbol, the name of  
a function to be called when the namespace is "invoked" as a script.

         - I propose ":run" (with no default so no namespace is ever  
accidentally executable.)

         - If provided, the :run value in the ns form is stored as  
the :run value in the namespace's metadata.

         - The run function should

             - accept/expect Strings as arguments

                 - it may accept any number of Strings using normal  
Clojure arity rules.

             - return an Integer

                 - zero indicates success

                 - non-zero indicates (some kind of) failure

                     - error codes should be chosen and documented by  
the namespace author

     - add a function to clojure.core to run an executable namespace:

         - (run ns-name arg*)

             - requires the namespace

             - retrieves the namespace's :run value and resolves it to  
a var in the namespace.

             - calls that function via:
                 (apply the-run-func args)

             - returns the integer that the-run-func returns

     - add support to clojure-main to handle a namespace name in  
"script position" by:

         - calling "run" on it, passing *command-line-args* in for the  
args.

         - returning the status value to the OS via Java's System/exit  
facility

This would be a change away from the current handling of scripts which  
simply loads them, expecting them to do their operation at load time.  
It also requires the resource (file) that contains the script be in  
classpath. (Though that's easy to accomplish by adding "<path-to-the- 
script>" to classpath.)

With this method, we have a new standard way to invoke a script, and  
the scripts we load this way are "clean" in the sense that they don't  
run arbitrary code while loading. (They can, of course, run arbitrary  
code during macro expansion but it's still a good idea not to have  
bits of executable code laying around being loaded when the  
namespace's definition is loaded.)

A clear separation for scripts between load time and run time is a win.

Also with this method, we can treat (specially marked) namespaces that  
have already been loaded into Clojure as runnable entities (albeit  
with a rather restricted interface for arguments and return values).  
We can invoke them from other Clojure code as we would in a shell  
script, but without involving the OS at all.

Via string arguments, these Clojure scripts could also indicate/react- 
to a desire to use *in* *out* and *err* to communicate as  
corresponding UNIX tools would when run by the shell.

People who want to "run code from a file" will still be able to do so  
by using "load", but that would no longer be the supported mechanism  
for writing/using/invoking Clojure scripts.

Thoughts?

--Steve

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to