You can define a variable in the current scope using a let
keyword and an equal sign.
> let foo = 42
> echo! foo
42
There are several variable scopes in Replicant:
- System scope – all the executables that exist in the current
PATH
and all the environmental variables - Language scope – functions, variables and utilities defined by Replicant
- Script / REPL scope – variables defined at the top level of a script or a REPL
- Local scopes – variables defined inside blocks
By defining a variable in an inner scope, you can shadow a variable in an outer scope.
> echo! git
{ clone: Command, init: Command, ... }
> let git = 42
> echo! git
42
> if true {
. let git = 3
. echo! git
. }
3
> echo! git
42
All variables from the system scope can be accessed using the sys
object. That way, you always have access to system commands even if they are shadowed by Replicant language features or your own local variables.
> let cd = 42
> cd! `..`
TypeError: cd is not a command, but a value: 42
> sys.cd `..`
> # Success!
In Replicant, executing commands is akin to calling functions in traditional programming languages. Commands are a first-class types – that means you can save them into variables etc. To execute a command, you need to append an exclamation mark to its name.
> echo! 42
42
> let print = echo
> print! 42
42
> mkdir! `foo`
> cd! `foo`
> git.init!
Initialized empty Git repository in ~/foo/.git/
Commands can have positional arguments and/or named arguments. To supply positional arguments to a command, simply list them one by one after the exclamation mark. Named arguments are specified using the --name
or --name=value
syntax.
> # No arguments
> git.init!
> # Positional arguments
> cp! `../my-file.txt` `README.txt`
> # Named argument without value
> git.add! --all
> # Named argument with value
> git.commit! --message="my initial commit"
Apart from arguments, commands can also recieve data using the so-called input. The main difference is that arguments have to be passed while executing the command, whereas input is a stream of data that can be supplied throughout the lifetime of the running program. If you execute a command which needs input but you don't provide it, the program will pause and you'll be asked to enter the required data. Similarly, a command can output data while it's running. By default, the data will be printed out for you. However, you can also connect an output of one command to the input of another command – this is called piping and it's a tool that will make you much more productive once you learn it.
TODO