Importing things

Keeping with the theme of reusing code that we introduced when talking about variables, Ryan also you to use a whole .ryan file as a kind of variable in other .ryan files. Imagine that you have a big projects in which you need to have many .ryan files for many different purposes. Normally, it's the case that you need to share a couple of variables between all these files, for example a username or a link to a shared resource. It would be a pain to have to redeclare it in every file, not to say dangerous if you ever need to change its value.

To cope with this scenario, you could create a common.ryan with all the repeated values and then, in each file, just do

let common = import "common.ryan";

This will evaluate the contents of common.ryan and put it in the variable common. The file common.ryan must be in the same directory as the file containing the import statement, otherwise Ryan won't be able to find it. If your file is in another path, you can use your operational system's path system to locate it:

let common = import "../other-stuff/common.ryan";       // e.g, in Linux and MacOS
let common = import "C:\Users\ryan\stuff\common.ryan";  // e.g, in Windows

Customizing your files

Another very common use-case for imports is to customize the JSON generated by your .ryan depending on the environment it is executed in. For example, it's very common for programs to be configured differently when testing than when put to run "for real" (also called the production environment). Usernames, passwords and resource names will be completely different to avoid a rogue test to ruin the operation of your system.

Enter environment variables: these are a set of variables that your operational system passes to every program in order to customize its behavior. Of course, you can set these variable directly through the command line:

MY_VAR=1 ryan < my_file.ryan

This will invoke ryan on my_file.ryan with MY_VAR set to 1. You can then access this variable from your .ryan file as so:

let my_var = import "env:MY_VAR";   // my_var will be set to `1`.

You can pass whole Ryan programs in environment variables, if you wish, although it may not be the most comfortable thing to do. The only restriction is that such programs can only access other environment variables; it cannot touch your files anymore. Therefore, this will not work:

MY_PROG='import "common.ryan"' ryan < my_file.ryan

If my_file.ryan tries to import "env:MY_PROG", an error will be raised.

Importing chunks of text

Up to now, we have only talked about importing Ryans from Ryans. However, in many cases, it is very quite to import text directly, verbatim. Ryan saves you the trouble of writing quotations and escape sequences by allowing you to import things as text:

let gilgamesh = import "tale-of-gilgamesh.txt" as text;

You can also use this with environment imports:

let username = import "env:USERNAME" as text;       // username is `"Ryan"`.

If USERNAME is set to Ryan, without the as text, you would get an error: after all the variable Ryan has not been set in your USERNAME program. With the as text, Ryan will understand that we only want the string "Ryan".

Setting defaults

If the imported file does not exist or the environment variable is not set, Ryan will, by default, raise an error. You can provide a default value to override this error using or:

import "env:FORGOT_TO_SET" or "Ryan";   // -> "Ryan"
import "does-not-exist.ryan" or {};     // -> (empty dictionary)

The clause or will force the import to use the default value if, for any reason whatsoever the import fails.

Limitations

No dynamic imports

Although import expects a string as input, you cannot use an expression that yields a string; the import must be only a literal string. This will not work:

import "my-" + "file.ryan"

nor will this

let num = 4;
import `file-${num}.ryan`

You can however, go around this limitation in many cases. For example, you can use if ... then ... else ... for conditional imports:

if 1 == 1 then
    import "this.ryan"
else
    import "that.ryan"

Even though import does not accept expressions, it can be freely used within expressions to allow for some level of customization.

No circular imports

Circular imports will result in a nasty, smelly error:

// tweedledee.ryan:
import "tweedledum.ryan"

// tweedledum.ryan
import "tweedledee.ryan"

If you ever find yourself in this situation, you will need to restructure your files in order to destroy the cyclic dependency. If a and b depend on each other, you can to put the "depended" part in a third file c and make both a and b depend on this file instead. This "third file trick" solves most, if not all, situations you might encounter.