Skip to content

Instantly share code, notes, and snippets.

@mjambon mjambon/typed-shell.sh

Last active May 7, 2020
Embed
What would you like to do?
Daydreaming about a shell syntax that embeds ocaml code and vice-versa
# Guiding principle: a shell is a domain-specific language for handling
# files and processes. These core features shall be served by a dedicated
# and already familiar syntax. Traditional programming control structures
# on the other hand are hard to use and limited in traditional shell
# languages. For this, we shall use a proper programming language, one that
# checks statically for basic programming mistakes, allows the use of rich
# data structures, and integrates well in large applications.
#
# Language properties:
#
# - shell-friendly syntax following the principle of minimum surprise:
# if it looks like valid shell syntax, it must either behave like a user
# would expect from a shell or let the user know it's invalid.
# - compiles to plain OCaml (or other typed language).
# - interoperates easily with the target language (OCaml), allowing
# mixing of files in the same project.
# - mixing syntaxes in the same file should be possible, but not required
# for simple shell scripts.
# - can produce a standalone executable.
# - allows typed function definitions, i.e. wrapping around external
# commands.
# - 'ocaml' is a reserved keyword in shell and 'shell' is a reserved
# keyword in ocaml.
# - shell commands may be created in pure ocaml, without calling an
# external executable.
# - the arguments of shell commands are not typed, due to the simple shell
# syntax. They're the usual argv array of strings.
# Here we put common external commands in scope (cat, cp, ls, ...). They are
# to be found in $PATH at call time. These are ocaml values of type 'cmd'.
# For example, the 'Shell.Coreutils.ls' ocaml value has type 'Shell.cmd',
# which can be used as command names in shell pipelines.
#
ocaml { open Shell.Coreutils }
# Non-standard commands must be declared. The 'cmd' syntax used below
# expands to ocaml code like 'let nano = Shell.wrap_command "nano"':
#
cmd nano
# Define an arbitrary function (embed a plain ocaml snippet)
ocaml {
let add a b =
a + b
}
# Define a function that itself embeds shell syntax
ocaml {
let find_filenames dir =
(* 'readlist' is a shell builtin similar to 'readarray' in bash.
Returns an ocaml value of type 'string list'. *)
shell {
find dir | xargs basename | sort -u | readlist
}
}
# Type error: 'add' is not a shell command.
echo 'yo' | add
# Prompt user.
echo -n 'Enter file name: '
# Read a line of input.
outfile=$(head -n1)
# Write something to a file.
# Note: no need for quotes around '$outfile', even if it contains
# whitespace (just a shell wart we're getting rid of).
#
echo 'hello' > $outfile.txt
echo world >> $outfile.txt
# Write a loop.
ocaml {
let lines = shell { readlist < $outfile.txt } in
List.iter (fun line -> Printf.printf "line: %s\n" line) lines
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.