Last active
August 20, 2020 08:16
-
-
Save mjambon/bb07b24f89fa60c973735307ce9c6cb9 to your computer and use it in GitHub Desktop.
Daydreaming about a shell syntax that embeds ocaml code and vice-versa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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