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-lwere all symbols in the above examplewhich 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 functionexitdefined in the modulelibc: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"andfoo(bar (1 + 2)would not help improve code comprehension.whitespace is important:
a:=10is requesting the value of a variable called
a:=10and 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
ain theprintfexpression different to thefind,fooandwcin the first?Answer: it isn’t. If a potential variable name is bound to a value, here,
awas bound to10, 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,-nameandfooeach 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
PATHfor an external command calledfind.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
/usrandfoocan’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 argsincluding things like infix arithmetic operations:a + 1
where the reader spots the infix operator
+and does a little rearranging to the “normal”cmd argsform:+ 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.txtpart is seen by the reader asfoo . txtand is rearranged as the sub-expression(value-index foo txt)wherefooandtxtare perfectly good (potential) variable names.They almost certainly aren’t variable names, though, and
value-indexwill complain thatfoois 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
ron 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:
lsdoesn’t run the external command ls but instead returns the value of the variable
lswhich, in all probability, resolves to the symbolls.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)