scsh-users
[Top] [All Lists]

Re: Perl, English, syntax for Scheme and shells (was Re: scsh in scm ...

To: scsh@martigny.ai.mit.edu
Subject: Re: Perl, English, syntax for Scheme and shells (was Re: scsh in scm ...)
From: shivers@ai.mit.edu (Olin Shivers)
Date: 10 Jan 1996 20:21:45 -0500
Organization: Artificial Intelligence Lab, MIT
Reply-to: shivers@ai.mit.edu
   From: wilson@cs.utexas.edu (Paul Wilson)
   Newsgroups: comp.lang.scheme,alt.lang.scheme.scsh
   Date: 2 Jan 1996 15:49:29 -0600

   I think the evolution of natural language has interesting lessons to teach
   about programming language design. ... Another lesson is
   that if you don't *design* the language well, it will be a painful
   kludge that will cause unnecessary suffering for a very long time.

Yes!

   So what do we make of this?  What useful lessons can we learn from
   Perl and common shell languages, without having a Scheme-based shell
   degenerate into a whole bunch of syntactic quirks?

   What do we look for in a syntax for a shell language, including one
   that's typed at an interactive prompt?  I think the parenthophobes 
   hold sway, rightly or wrongly, and not *entirely* wrongly.

My answer is twofold:
- Exploit the general s-expression framework of Scheme to embed targetted
  sublanguages, such as loop expressions, Awk control structure, or
  process-control notation, within a general-purpose language. This
  way, you can "pay as you go" for your syntactic quirks, as long as
  you are willing to live within the parenthesis paradigm.

- Design a simple, terse, non-parenthesized notation for interactive
  use, but provide escapes in this notation to full-blown Scheme.
  Now simple pipes, redirections, program invocation, job control,
  and so forth are as easy as in csh, but if you want something even
  as complex as a conditional or a loop -- escape to Scheme.

   Tcl has horrid rules for when things are evaluated and
   when they are not, with the wrong defaults.  (Like many shell languages,
   you have to force evaluation of a variable, because the default is that
   things are not evaluated.  I prefer Lisp and Scheme's default, which
   is that things *are* evaluated, which would be a problem in that lots
   of literals would have to be quoted, except that many common literals
   are "self evaluating."  Apparently Ousterhout and most shell language
   designers have never caught on to self-evaluating literals, because
   they never learned Lisp.  So you end up with things like [set $a $a+1].
   I firmly believe that Scheme is a better basis for such things, if
   we come up with some handy syntax that can easily be explained.

Actually, I think the literal-by-default choice is better adapted to shells
than the variable-by-default choice. Most tokens in my interactive
commands -- and even in my /bin/sh shell scripts -- are intended as
constant file-names, program switches, or arguments. If you notice,
I *kept* this design decision in scsh. You have to use comma in the
process notation to get a variable:
        (if (zero? (run (mail ,user) (<< ,msg)))
            ...)
Scheme is cool because I'm allowed two notations here. Outside the RUN
form, we have Scheme, where literals like ZERO? are variables. Inside
the RUN form, we have process notation, where literals like MAIL are
constants used to search the file system for programs. When I'm using
process notation, I have to mark my vars with commas: ,USER and ,MSG.
Each notation is adapted for its intended use.

      1. Can we come up with a command syntax that's nice and terse for
         scripting purposes, but doesn't make it hard to write larger
         routines?  Ideally, I'd like a syntax that works for both
         scripting and "real" programming, so I don't have to switch
         syntaxes.  (Naturally, there would be no problem of interoperability
         of code written in different Scheme syntaxes---it's just sugar.)

I claim scripting is real programming. Do this. Do that. Branch. Loop.
It's not worth it to try and tease these two apart; they blur into each other.

      2. How do we distinguish between calls to built-in or user-defined
         Scheme functions and commands that are sent to programs outside
         Scheme?  For scsh, you currently have to explicitly indicate
         that you want to run a UNIX program by using a (run ...) form
         or whatever.  I think this may be the right default for 
         nontrivial programs, but for many scripts it's awkward.  Somebody
         (John Ellis?) did a Lisp shell several years ago, where any
         unbound function name was assumed to be an indication that
         the programmer meant to call a UNIX program.  So, for example,
         if rm was bound, (rm foo) meant call the rm procedure, but if
         rm was unbound, the shell would look for a unix program named
         rm, and call it with the argument foo.

Argh. This is the fundamental error of just about everyone who tries the
functional-language-Unix-shell game. I discuss this at length in my paper on
scsh. (See the section on the Tao of Scheme and Unix.) The two paradigms do not
match semantically, so trying to match them up syntactically is bound to
lose. (BTW -- if you want to track down the Ellis paper Paul mentions above,
my scsh paper cites it.)
        -Olin

<Prev in Thread] Current Thread [Next in Thread>