Idio User Guide

Idio is a programming language for orchestrating commands like a shell. It is asking the question, can we write better shell scripts by integrating shell-like constructions into a programming language?

It is a programming language first and foremost so not all traditional shell command syntax translates directly.

Also, as a programming language, it is intolerant of failure. No more shell scripts where errors slide casually by.

A few pointers:

  • normal shell command syntax:

    find /usr -name foo | wc -l
    

    that is, cmd args, with no extraneous parentheses or commas. That applies to the programming language proper, not just the “shell” syntax

  • (potential) variable names, ie. symbols, can be pretty flexible so /usr, -name, | and -l were all symbols in the above example

  • which includes what would have been the shell meta-characters * and ?

    • ? is used for predicate names, for example, symbol?

    • * is conventionally used to bookend global names

    Going further:

    • ! is conventionally used to suffix mutation functions, for example, set! (whose infix operator is the rather more familiar =)

    • / is used to join module and value names if you use the name “directly”, for example, libc/exit, to call the function exit defined in the module libc

    • : is the prefix for keywords

    You get the picture, names can have punctuation characters in them to help communicate ideas and intent.

    There are limits on punctuation characters in symbols, mostly to prevent syntactic confusion: foo"bar "hello" and foo(bar (1 + 2) would not help improve code comprehension.

  • whitespace is important:

    a:=10
    

    is requesting the value of a variable called a:=10 and isn’t defining anything (assuming you could discern the := definition operator in the mix)

    This is the exact opposite of the shell which demands that there is no whitespace in an assignment.

  • normal programming variables – no sigils:

    a := 10
    
    printf "a is %d\n" a
    
  • Wait! How is the use of a in the printf expression different to the find, foo and wc in the first?

    Answer: it isn’t. If a potential variable name is bound to a value, here, a was bound to 10, then you’ll get the value, otherwise variable names resolve to “themselves”, symbols, and carry on life as “values which happen to be symbols” until something wants to use them.

    In the case of the pipeline, the system eventually decides to invoke (the symbol) find – with arguments /usr, -name and foo each of which are (probably) symbols themselves.

    A symbol isn’t invokable (it’s not a function) so the system will break out of programming language mode and go into shell mode and search PATH for an external command called find.

    Symbols have a fairly obvious translation into a string as the arguments for the external command.

    Tip

    Try to use strings for filenames, so "/usr" and "foo" in this case.

    That isn’t because /usr and foo can’t be translated into strings for an external command but because none of the redirection operators are expecting symbols as arguments so it’s a good habit to be in.

  • everything is cmd args including things like infix arithmetic operations:

    a + 1
    

    where the reader spots the infix operator + and does a little rearranging to the “normal” cmd args form:

    + a 1
    

    before the evaluator gets to work.

    Now the + is in “functional position”, ie. cmd, and will be resolved to be the function that adds numbers together.

    The | in the first example was also identified as an infix operator and “a little rearrangement” took place.

  • infix operators can be a bit annoying:

    zcat file | tar xvf \-
    

    Without the \, the - is being treated as an arithmetic infix operator and so the reader is waiting for the second argument. Here we can escape the - from the clutches of the reader and get on with our day.

  • the other annoying infix operator is ., used to index into compound values (arrays, structures etc.)

    If you forget to use strings for filenames you can get caught out:

    find /usr -name foo.txt | wc -l
    

    where the foo.txt part is seen by the reader as foo . txt and is rearranged as the sub-expression (value-index foo txt) where foo and txt are perfectly good (potential) variable names.

    They almost certainly aren’t variable names, though, and value-index will complain that foo is not indexable.

    You could escape it like before:

    find /usr -name foo\.txt | wc -l
    
  • one expression per line

    You cannot put more than one expression on a line, there is no statement separator or, in Bash-speak, control operator like Bash’s || & && ; ;; ;& ;;& ( ) | |& <newline>.

  • however, the reader will extend an expression across lines until it gets a matching brace, parenthesis or bracket or the matching string delimiter

    Strings can be multi-line.

    This leads to a normal form for defining functions:

     1define (add a b) "
     2add `a` to `b`
     3...
     4" {
     5  ;; calculate a result in a local variable
     6  r := a + b
     7
     8  ;; return the result
     9  r
    10}
    

    Here the string-opening " on line 1 means the documentation string is not complete until the matching " on line 4 where the { before the end of line means the body is not complete until the matching } on line 10.

  • single element expression problem

    The r on line 9, above, is clearly returning the result of the addition. That’s idiomatic of a programming language (well, many programming languages).

    With our shell-hats on, though, that means that:

    ls
    

    doesn’t run the external command ls but instead returns the value of the variable ls which, in all probability, resolves to the symbol ls.

    To force the execution of ls, that is, instead of resolving a value we want to force the invocation of a single element expression we need to wrap it in parentheses:

    (ls)
    

    Annoying. It only affects single-word external commands (although make, env, sort spring to mind).

    If you pass any arguments then it must be a function invocation and the system can figure out it needs to execute ls.

  • everything returns a value

    Everything returning a value is a thing and turns out to be useful. Who knew?

  • there is deliberately no support for interactive sessions

    Idio is about better shell scripting.

    There are far better interactive shells out there than Idio might ever be including ancillary software like rlwrap.

    So, use rlwrap and realise it’s not important.

    Idio has normal Job Control functionality in respect of controlling terminals, Process Group IDs etc. just no support whatsoever for interactivity beyond terminal cooked mode.

    Remember, Idio is intolerant of failure and the default behaviour is to quit when an external command fails.

    That is quite annoying in interactive sessions so those have this behaviour suppressed (albeit re-asserted in pipelines).

Before we go on and get lost in the details, here’s a

Warning

Idio is

experimental

some of the syntax and features fit in the “let’s see how this goes” pattern

immature

yes, there are bugs and plenty of them

there are longhand forms that need shorthand syntaxes

it needs to be used in anger more

incomplete

as a programming language, Idio is far from complete and even as a shell there are several missing pieces

Now, that’s not a great sales pitch but it really is just a warning that Idio is new and hasn’t seen enough of the world not to be too fragile in the face of pesky users.

That said, Idio is quite usable and, in fact, is used to generate it’s own code during a build as well as much of the boilerplate code of interfaces into libraries and the reStructuredText of the Reference Manual.

Let’s dig into some detail.

Last built at 2026-01-04T22:40:02Z+0000 from da47fd3 (dev)