Warning: Work in progress! Leave feedback on Zulip or Github if you'd like this doc to be updated.

Procs, Blocks, and Funcs

Procs are shell like-functions, but they can have parameters, and lack dynamic scope.

proc foo(x) {
  echo "[$x]"

foo bar  # prints [bar]

Blocks are fragments of code within { } that can be passed to builtins (and eventually procs):

cd /tmp {
  echo $PWD  # prints /tmp
echo $PWD  # prints original dir
Table of Contents
Procs Can Be Open Or Closed (With a Signature)
Proc Signatures
Block Syntax
Block Semantics
User Execution (like Ruby's yield keyword?)
User Evaluation (e.g. for Config Files)
Control Flow
Setting Variables in Enclosing Scope
User-Defined Functions are Deferred
Two Kinds of Composition

Procs Can Be Open Or Closed (With a Signature)

Shell-like open procs that accept arbitrary numbers of arguments:

proc open {
  write 'args are' @ARGV
# All valid:
open 1 
open 1 2

Stricter closed procs:

proc closed(x) {
  write 'arg is' $x
closed      # runtime error: missing argument
closed 1    # valid
closed 1 2  # runtime error: too many arguments

Proc Signatures


Block Syntax

These forms work:

cd / {
  echo $PWD
cd / { echo $PWD }
cd / { echo $PWD }; cd / { echo $PWD }

These are syntax errors:

a=1 { echo bad };        # assignments can't take blocks
>out.txt { echo bad };   # bare redirects can't take blocks
break { echo bad };      # control flow can't take blocks

Runtime error:

local a=1 { echo bad };  # assignment builtins can't take blocks

Caveat: Blocks Are Space Sensitive

cd {a,b}  # brace substitution
cd { a,b }  # tries to run command 'a,b', which probably doesn't exist

Quoting of { } obeys the normal rules:

echo 'literal braces not a block' \{ \}
echo 'literal braces not a block' '{' '}'

Block Semantics

TODO: This section has to be implemented and tested.

User Execution (like Ruby's yield keyword?)

proc p(&block) {
  echo '>'
  $block    # call it?
            # or maybe just 'block' -- it's a new word in the "proc" namespace?
  echo '<'

# Invoke it
p {
  echo 'hello'
# Output:
# >
# hello
# <

User Evaluation (e.g. for Config Files)

How to get the value?

var namespace = evalblock('name', 1+2, up=1)

# _result is set if there was a return statement!

# namespace has all vars except those prefixed with _
var result = namespace->_result

TODO: Subinterpreters?

Control Flow

Setting Variables in Enclosing Scope

Can block can set vars in enclosing scope?

setref('name', 1+2, up=1)


In addition to shell-like procs, Oil also has Python-like functions:

var x = len(ARGV) + 1

User-Defined Functions are Deferred

For now, we only have a few builtin functions like len().

Two Kinds of Composition

There are two kinds of composition / code units in Oil:

procs are called with a "command line":

myproc arg1 arg2 arg3

funcs are called with Python/JS-like Oil expressions:

var x = myfunc(42, 'foo')
do myfunc(42, 'foo')   # throw away the return value.

This is NOT legal:

myfunc(42, 'foo')


Shell is really the "main", if that makes sense. procs can call funcs, but funcs won't be able to call procs (except for some limited cases like log and die).

People may tend to prefer funcs because they're more familiar. But shell composition with proc is very powerful!

They have at least two kinds of composition that functions don't have. See #shell-the-good-parts on Bernstein chaining and "point-free" pipelines.

Here are some complicated examples from the tests. It's not representative of what real code looks like, but it shows all the features.


proc name-with-hyphen (x, y, @names) {
  echo $x $y
  echo names: @names
name-with-hyphen a b c


shopt -s oil:basic

func f(a, b=0, ...args; c, d=0, ...named) {
  echo __ args: @args
  echo __ named:
  echo @named | sort
  if (named) {
    return [a, b, c, d]
  } else {
    return a + b + c + d
var a = [42, 43]
var n = {x: 99, y: 100}

echo ____
echo string $f(0, 1, ...a, c=2, d=3)

# Now get a list back
echo ____
echo array @f(5, 6, ...a, c=7, d=8; ...n)

Generated on Thu Aug 20 11:57:53 PDT 2020