60 PicoLisp Functions You Should Know - 3: Input/Output
Tags: picolisp, lisp, functional
Published: 2021-10-19 09:12:17

Welcome back, let's continue our **60 most common PicoLisp functions** series!

Today we will talk about **Input and Output** functions and how to print the notorious "Hello World" string we have all been waiting for.

-------------------

*All examples can be executed in the PicoLisp [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop). If you want to follow along, type ``pil +`` in the console to open the REPL. Check [here](https://picolisp-blog.hashnode.dev/60-picolisp-functions-you-should-know-1-arithmetics) for further basic commands.*

-------------------

### Printing

To print output in PicoLisp, there are a number of functions depending on the need: With or without spaces between the arguments, with or without new lines...

Let's start with a simple low-level text output: ``prin`` prints all arguments without adding any spaces or new lines. ``prinl`` works the same way, except that a new line is added to the end (the "l" stands for "line").

Let's call each function two times in a row to see the difference:

```
: (prin "Hello World") (prin ( 1 "abc" (d e (f)) 999 ghi))
Hello World1abcdef999ghi

: (prinl "Hello World") (prinl (1 "abc" (d e (f)) 999 ghi))
Hello World
1abcdef999ghi
```

As you can see, the printout of the list ``(1 "abc" (d e (f)) 999 ghi))`` is returned without any spaces or parentheses: ``1abcdef999ghi``. The inner structure of the list is lost. By looking at the output, we have no idea where one list item ends and the next one begins, and if the structure is linear or nested.

If we want to keep this information, i. e. if we need to print **complete Lisp expressions** that can be passed over to other functions, use ``print`` and its variations: If you need a space after the printing output, you can use ``printsp``, for new-line use ``println``.  

Notice the differences:

```
: (print "Hello World") (print (1 "abc" (d e (f)) 999 ghi))
"Hello World"(1 "abc" (d e (f)) 999 ghi)

: (printsp "Hello World") (printsp (1 "abc" (d e (f)) 999 ghi))
"Hello World" (1 "abc" (d e (f)) 999 ghi

: (println "Hello World") (println (1 "abc" (d e (f)) 999 ghi))
"Hello World"
(1 "abc" (d e (f)) 999 ghi)
```

This time the list structure is maintained during the printing process. 

-------------------------------------

### Reading from console and files

The read function is called ``read``. According to the documentation, it reads from the **current input channel**. But what is our current input channel?

If we use the REPL, then the current input channel is the **console**. However, if we call a PicoLisp script, then the current input channel is the script's **file**. Thus, if we want to write a script that reads from the console, we need to change the current input channel. 

We will come back to this point later when we talk about scripts. For now, let's stick with the REPL.

-------------------------------

See the following example of the ``read`` command:

```
: (list (read) (read) (read))
123
abcd
hello
-> (123 abcd hello)

: (list (read) (read) (read))
A B C
-> (A B C)
```

As you can see, we take three parameters from the console and make a list out of them using the ``list`` function. Space and new line both are recognized as delimitors.

To read only a single character, use ``char``:

```
: (char)                   # Read character from console
A                          # (typed 'A' and a space/return)
-> "A"
```

To read in a line, use ``line``. You can additionally hand over a ``NIL`` or ``T`` flag. If the flag is ``NIL`` a list of single-character strings is returned, otherwise it is returned as one string.

```
: (line)             # equivalent to (line NIL)
abcdefghijkl
-> ("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l")

: (line T)
abcdefghijkl
-> "abcdefghijkl"
```

Additionally, you can add ``cnt`` arguments that specify how the input should be grouped. If the ``NIL`` flag is used, the input is grouped the single-character sublists, while the ``T`` flag groups to substrings:

```
: (line NIL 1 2 3)
abcdefghijkl
-> (("a") ("b" "c") ("d" "e" "f") "g" "h" "i" "j" "k" "l")

: (line T 1 2 3)
abcdefghijkl
-> ("a" "bc" "def" "g" "h" "i" "j" "k" "l")
```

-------------------------------

Next let's check how to **read** from a file in the REPL. Create a file "test.txt" and write some arbitrary content inside, such as:

```
<test.txt>

hello world
this is a test
1234567890
```

Now we **change the input channel to the file** by ``(in <filename>)`` and repeat the example from above - i.e., read in the first three **items** and make a list:

```
: (in "input.txt" (list (read)(read)(read)(read)))
-> (hello world this is)
```

We get the first four words back. Like in the console example above, space, and new-line worked as delimitors.

------------------------

### Writing Output to a File

Similarily, we can write to a file "output.txt" by using ``(out <filename>)``.

```
: (out "output.txt" (println 123 '(a b c) 'def))
-> def
```

Now we will find a file called "output.txt" in our current folder. Let's check its content:

```
$ cat output.txt
123 (a b c) def
```

It worked!

------------------------

### External Libraries

An **external library** is basically a file with a collection of pre-defined functions bundled together. The ``pil21``installation comes with some library files which are located in the ``lib`` folder of your ``pil21`` installation (of course you can also create your own). 

To read in **external library files**, the function ``load`` is used:

```
(load "@lib/http.l" "@lib/xhtml.l")
```

The ``@`` stands for the relative path to your ``pil21``-Installation folder.

As an example, let's take a look into the ``lib/math.l`` file. 

![math.l.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631196355627/F06K9fbLr.png)

We see a ``setq`` for the variables ``pi`` and ``pi/2``. Now let's load ``math.l`` into our REPL and see if we can access these:

```
: pi     # pi is not known
-> NIL

: (load "@lib/math.l")
-> atan2
: pi      # pi is known
-> 3141593
```

It works! 

*You probably expected ``pi`` to be 3.141593 instead of 3141593. As briefly mentioned in the [Arithmetic Functions](https://picolisp-blog.hashnode.dev/60-picolisp-functions-you-should-know-1-arithmetics) post, PicoLisp calculates internally with fixed point integers and a defined **scale**. We will come back to that point in one of the next posts.*

-------------------

These were the basics of input and output functions in PicoLisp. We will continue with conditionals and loops in the next section.

-------------------

### Sources

- Cover: http://grade5eisnor.weebly.com/inputoutput-machines.html   
- https://software-lab.de/doc/index.html  
- https://software-lab.de/doc/tut.html