# PicoLisp Explored: The task function

Before continuing with the ["Mobile Development"](https://picolisp-explored.com/an-openstreetmap-app-written-in-picolisp-pt-1) series, I decided to write about the ``task`` function, because it is a very useful function as we will see in the mobile app series.

---

### Realizing repetitive tasks with ``task``

With help of ``task``, we can schedule tasks to be executed. This can be useful in many situations, like when we want to fetch data periodically from the server, or if we are waiting for an input stream via a socket.

We check the [docs](https://software-lab.de/doc/index.html):

> ``(task 'num ['num] [sym 'any ..] [. prg]) -> lst``

> A front-end to the ``*Run`` global. If called with only a single num argument, the corresponding entry is removed from the value of ``*Run.`` Otherwise, a new entry is created. If an entry with that key already exists, an error is issued. For negative numbers, a second number must be supplied. If sym/any arguments are given, a job environment is built for the ``*Run`` entry. 

---

### A simple counter

This is a lot of information, so let's look at the most straightforward example first: Printing a counter 1, 2, 3, 4, 5.... We want to print **one number per second** (i. e. 1000 ms), and we want to start immediately (i. e. after 0 ms).

The syntax for this is as follows:

```
: (task -1000 0 (tty (println (inc (0)))))
```

The first number defines the frequency, the second number defines the delay until the command is first executed. The first number needs to be **negative**.

- *Note 1: we use ``(tty (println ...))`` instead of only ``println``, because this will keep the command prompt in the REPL stable, as you can see in the following gif:*


![task_count.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1650969144806/h8LgZ-G2m.gif)

   *Without ``tty``: The command prompt gets overwritten by the ``println``-output, the result is very messy.* 



![task_count-notty.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1650969336473/0Q4m04_Ya.gif)

- *Note 2, for PicoLisp enthusiasts: ``(inc (0))`` **destructively** increments the cell which initially contains ``0``. For this reason, the return value will constantly increase. Note the difference to ``(inc 0)``, which will always return ``1``.*


---

Now how can we stop the counter? As the documentation tells us, the first number servers as identifier. This means in order to stop this task, we can simply call:

```
(task -1000)
```

---

### The ``*Run`` global variable

As the docs tell us, ``task`` is only a front-end to the global variable ``*Run``. So let's execute task and check the content of ``*Run`` to understand what that means.

If **no** task is running, ``*Run`` is ``NIL``:

```
: *Run
-> NIL
```

Now let's set a task and try again:

```
: (task -10000 -10000 (tty (println (inc (0)))))
-> (-10000 -10000 (tty (println (inc (0)))))
: *Run
-> ((-10000 -8102 (tty (println (inc (0))))))
```

We have set a task that fires after 10 seconds and then every ten seconds. If we check ``*Run``, we see that the second item of the list seem to contain the time difference up to the next execution, as well as the current values:

```
1
2
3
4
5
: *Run
-> ((-10000 -1251 (tty (println (inc (5))))))
6
: 
```


---

### Conditional Execution of ``task``

Now let's extend our little example: We want to count down from 10 to 0, after that the task should be stopped.

``task`` implicitly defines a **closure** (read here for more on [PicoLisp closures](https://picolisp-explored.com/picolisp-explored-the-curry-function)). This means we can also execute more complex functions:

- first we set ``N`` to 10,
- then we print it,
- then we decrement it with ``(dec 'N)``
- then we check if the result is equal to 0.
- If it is equal to zero, the task is stopped.

In other words:

```
: (task -1000 0 N 10 (tty (println N)) (and (=0 (dec 'N)) (task -1000)))
-> (-1000 0 (job '((N . 10)) (tty (println N)) (and (=0 (dec 'N)) (task -1000))))
10
9
8
7
...
```

As you can see, ``task`` wraps a ``job`` function around the ``prg`` part of the task. ``job`` first takes a list with the current values of the symbols (in this case ``(N . 10)``), and then the program body which defines the new values of the symbols.

---

### Working with file descriptors

There is a second way to work with ``task``: Instead of a periodic time in milliseconds, we can define a **file descriptor**. File descriptors are unique identifiers for files or other input/output resources such as pipes or networks in Unix systems (["File descriptors" on Wikipedia](https://en.wikipedia.org/wiki/File_descriptor)).

The file descriptors 0, 1 and 2 are defined as Standard Input, Standard Output and Standard Error. After that, each new file or socket we open will define a new file descriptor, which is also the return value for the picolisp functions like ``open`` and ``port``.

```
: (open "myfile.txt")
-> 3
: (port 4444)
-> 4
```

*Note: ``port 4444`` opens a TCP port on 4444. For an UDP port, the second arguments should be ``T``: ``port T 4444``.*

---

Now what can we do with this? We can define a ``task`` which "listens" on a port and executes all commands that it receives:

```
: (task (port T 4444) (eval (udp @)))
-> (3 (eval (udp @)))
```

``(udp <FD>)`` **receives** one UDP package at the corresponding file descriptor, which is set to the socket at port 4444 in the example above.

Next, we can **send** data to this socket via  
 
```
: (udp "localhost" 4444 '(println *Pid))  # Send RPC message
-> (println *Pid)
```

and it will be **evaluated** because of the ``eval`` function defined in the ``task``.

---

In the next post, we will go a little bit deeper into the socket handling with task by exploring a very simple messenger app.


---

Sources

- https://en.wikipedia.org/wiki/File_descriptor
- https://software-lab.de/doc/index.html

