<commit_before># An overview of Furipota

Furipota is a discrete FRP DSL for describing better build pipelines. Incidentally, Furipota may also be used as a sort-of general purpose functional language and for shell scripting. The implementation uses a tree-walking interpreter running on Node, and uses JavaScript as an extension language. Furipota's performance itself is not great, but since all expensive computations are done in JavaScript directly, this isn't much of a problem.


## The model

So, what does "discrete FRP" mean anyway? [Functional Reactive Programming](http://stackoverflow.com/a/1030631), according to Conal Elliot, is a system of continuously evolving streams described through denotational semantics. In essence, it models values over time as a first-class concept, and allows one to combine them like any other kind of discrete value (e.g.: a number). The continuous part is at the core of the FRP model.

Furipota most definitely isn't that, so everywhere you see "discrete FRP", just understand it to be "a functional model mostly based on the definition and transformation of **discrete** streams of values over time". It's up to users to decide how to synchronise these streams, and determinism **is not** a part of the model.

So, even though I'm calling it "discrete FRP", the concept is much closer to [<NAME>'s reactive programming with observables](https://www.youtube.com/watch?v=sTSQlYX5DU0), than Conal Elliot's FRP. Note, however, that Furipota does not have the [hot vs. cold observables issue](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables) as streams are pure and only executed at the top level (although JavaScript plugins may mess this up).


## The functional part

Furipota is primarily a functional language, a bit like Haskell without the types. It's made out of very few primitive concepts:


##### Numbers

Furipota uses a single numeric type: 64-bit floating point numbers based on IEEE-754, just like JavaScript. Unlike JavaScript, numeric literals in Furipota may contain `_` characters to separate thousands:

```ruby
1_000.00  # ==> 1000
1000      # ==> 1000
```

##### Text

Furipota's textual type is called `Text`. Furipota has two ways of introducing textual data.

The raw text form:

```python
"""
None of the characters
"here" are intepreted
in any special way \no
"""
# "\nNone of the characters\n\"here\" are interpreted\nin any special way \\no\n"
```

And the interpolated text form, which uses `\` as an escape character, and `{...}` for embedding expressions:

```ruby
"2 + 2 is:\n {2 + 2}"
# => "2 + 2 is:\n 4"
```

Since Furipota is a more strict language about which kinds of values can be used in each function, text is only used for textual data meant to be readable by humans, not interpreted by programs.

##### Boolean

Logical values, either `true` or `false`.


##### Vector

Dynamic sequences of values. Vectors use the same syntax as JavaScript's Arrays, and are represented as JavaScript arrays underneath.

```ruby
[1 + 0, 1 + 1, 1 + 2]
# => [1, 2, 3]
```


##### Records

Pairs of symbols and values. Records are represented as JavaScript's objects, however Furipota ignores all prototypical delegation.

```ruby
-hello: 1  -world: 2
# => { "hello": 1, "world": 2 }
```

Empty records use the `{}` syntax for now, but will most likely move to a function in the future.

Properties of a record can be acessed with the `.` syntax:

```ruby
(-hello: 1).hello
# => 1
```


##### Functions

Finally, Furipota has functions. Functions are first-class computations that receive *exactly* two arguments: a positional argument of any type, and a *record* that provides options for that function.

```ruby
let say = (message @ options -> "Hello {message}")
in
say "world" @ -options: "here"
# => "Hello world"
```

The record is always optional, so one may always omit it:

```ruby
let say = (message -> "Hello {message}")
in 
say "world"
# => "Hello world"
```

Functions that look like multi-parameter functions, such as `1 + 2` or `Vector.concat [] [2]` are just 1-argument functions that return another function:

```ruby
let say = (name -> message -> "{name} says: Hello {message}")
in
(say "Quil") "world"
# => "Quil says: Hello world"
```

Because writing lambdas directly can be a pain for functions that take more than one argument, there's a syntax sugar for this:

```ruby
let say name message = "{name} says: Hello {message}"
in say "Quil" "world"
# (function application is left-associative, so parenthesis aren't needed)
```

Infix functions may also be defined. Furipota supports only `===`, `=/=`, `>`, `>=`, `<=`, `+`, `-`, `*` and `/` as valid infix names, but any function can be used as infix by preceding its name with `\``. 

```hs
let a + b = "{a}{b}"
in "hello" + "world"
# => "helloworld"

let cat a b = "{a}{b}"
in "hello" `cat "world"
# => "worldhello"
```

## Logical operators

The `and`, `or` and `not` operators are just regular infix and prefix functions. The `if...then...else` construct is an expression that evaluates the consequent or alternate depending on the value of the condition:

```ruby
if 2 < 3 then "less than" else "greater than"
```


## Tagged data

Furipota does not have classes or [tagged unions](https://en.wikipedia.org/wiki/Tagged_union), instead it uses the simpler concept of [open variants](https://caml.inria.fr/pub/docs/u3-ocaml/ocaml051.html). In Furipota in particular, an open variant is just a *tagged* value: a value and its associated "type identifier".

You create tags with the `^Tag predicates…` syntax, where a predicate is any function that returns `true` if the value at that position is valid, or `false` otherwise:

```ruby
define is-any x = true
define Tuple2 = ^Tuple2 is-any is-any
```

You construct tagged values by using the `make` function from the core library:

```ruby
import core "core"

define one-two = make Tuple2 [1, 2]
```

And you extract values with the `match...with` syntax, and the `^Tag` pattern. Note that the `Tag` here is an expression that returns a tag, not an arbitrary identifier:

```ruby
match one-two with
  case ^Tuple2 (let first) (let second) then first + second
  case ^((x -> x) Tuple2) any any then "same as above"
  default "nothing matched."
```

## Modules and definitions

A Furipota file is a module, and modules have a different grammar than expressions do. A module must start with a language version identifier, and may only contain declarations at its top level:

```ruby
%furipota/1

define hello = "world"
```

> Note that comments are valid before the version identifier, thus:
>
>     #!/usr/bin/env furipota-run
>     %furipota/1
>     import core "prelude"<commit_msg>Fixes documentation on match...with (removed let form)<commit_after># An overview of Furipota

Furipota is a discrete FRP DSL for describing better build pipelines. Incidentally, Furipota may also be used as a sort-of general purpose functional language and for shell scripting. The implementation uses a tree-walking interpreter running on Node, and uses JavaScript as an extension language. Furipota's performance itself is not great, but since all expensive computations are done in JavaScript directly, this isn't much of a problem.


## The model

So, what does "discrete FRP" mean anyway? [Functional Reactive Programming](http://stackoverflow.com/a/1030631), according to <NAME>, is a system of continuously evolving streams described through denotational semantics. In essence, it models values over time as a first-class concept, and allows one to combine them like any other kind of discrete value (e.g.: a number). The continuous part is at the core of the FRP model.

Furipota most definitely isn't that, so everywhere you see "discrete FRP", just understand it to be "a functional model mostly based on the definition and transformation of **discrete** streams of values over time". It's up to users to decide how to synchronise these streams, and determinism **is not** a part of the model.

So, even though I'm calling it "discrete FRP", the concept is much closer to [<NAME>'s reactive programming with observables](https://www.youtube.com/watch?v=sTSQlYX5DU0), than Conal Elliot's FRP. Note, however, that Furipota does not have the [hot vs. cold observables issue](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables) as streams are pure and only executed at the top level (although JavaScript plugins may mess this up).


## The functional part

Furipota is primarily a functional language, a bit like Haskell without the types. It's made out of very few primitive concepts:


##### Numbers

Furipota uses a single numeric type: 64-bit floating point numbers based on IEEE-754, just like JavaScript. Unlike JavaScript, numeric literals in Furipota may contain `_` characters to separate thousands:

```ruby
1_000.00  # ==> 1000
1000      # ==> 1000
```

##### Text

Furipota's textual type is called `Text`. Furipota has two ways of introducing textual data.

The raw text form:

```python
"""
None of the characters
"here" are intepreted
in any special way \no
"""
# "\nNone of the characters\n\"here\" are interpreted\nin any special way \\no\n"
```

And the interpolated text form, which uses `\` as an escape character, and `{...}` for embedding expressions:

```ruby
"2 + 2 is:\n {2 + 2}"
# => "2 + 2 is:\n 4"
```

Since Furipota is a more strict language about which kinds of values can be used in each function, text is only used for textual data meant to be readable by humans, not interpreted by programs.

##### Boolean

Logical values, either `true` or `false`.


##### Vector

Dynamic sequences of values. Vectors use the same syntax as JavaScript's Arrays, and are represented as JavaScript arrays underneath.

```ruby
[1 + 0, 1 + 1, 1 + 2]
# => [1, 2, 3]
```


##### Records

Pairs of symbols and values. Records are represented as JavaScript's objects, however Furipota ignores all prototypical delegation.

```ruby
-hello: 1  -world: 2
# => { "hello": 1, "world": 2 }
```

Empty records use the `{}` syntax for now, but will most likely move to a function in the future.

Properties of a record can be acessed with the `.` syntax:

```ruby
(-hello: 1).hello
# => 1
```


##### Functions

Finally, Furipota has functions. Functions are first-class computations that receive *exactly* two arguments: a positional argument of any type, and a *record* that provides options for that function.

```ruby
let say = (message @ options -> "Hello {message}")
in
say "world" @ -options: "here"
# => "Hello world"
```

The record is always optional, so one may always omit it:

```ruby
let say = (message -> "Hello {message}")
in 
say "world"
# => "Hello world"
```

Functions that look like multi-parameter functions, such as `1 + 2` or `Vector.concat [] [2]` are just 1-argument functions that return another function:

```ruby
let say = (name -> message -> "{name} says: Hello {message}")
in
(say "Quil") "world"
# => "Quil says: Hello world"
```

Because writing lambdas directly can be a pain for functions that take more than one argument, there's a syntax sugar for this:

```ruby
let say name message = "{name} says: Hello {message}"
in say "Quil" "world"
# (function application is left-associative, so parenthesis aren't needed)
```

Infix functions may also be defined. Furipota supports only `===`, `=/=`, `>`, `>=`, `<=`, `+`, `-`, `*` and `/` as valid infix names, but any function can be used as infix by preceding its name with `\``. 

```hs
let a + b = "{a}{b}"
in "hello" + "world"
# => "helloworld"

let cat a b = "{a}{b}"
in "hello" `cat "world"
# => "worldhello"
```

## Logical operators

The `and`, `or` and `not` operators are just regular infix and prefix functions. The `if...then...else` construct is an expression that evaluates the consequent or alternate depending on the value of the condition:

```ruby
if 2 < 3 then "less than" else "greater than"
```


## Tagged data

Furipota does not have classes or [tagged unions](https://en.wikipedia.org/wiki/Tagged_union), instead it uses the simpler concept of [open variants](https://caml.inria.fr/pub/docs/u3-ocaml/ocaml051.html). In Furipota in particular, an open variant is just a *tagged* value: a value and its associated "type identifier".

You create tags with the `^Tag predicates…` syntax, where a predicate is any function that returns `true` if the value at that position is valid, or `false` otherwise:

```ruby
define is-any x = true
define Tuple2 = ^Tuple2 is-any is-any
```

You construct tagged values by using the `make` function from the core library:

```ruby
import core "core"

define one-two = make Tuple2 [1, 2]
```

And you extract values with the `match...with` syntax, and the `^Tag` pattern. Note that the `Tag` here is an expression that returns a tag, not an arbitrary identifier:

```ruby
match one-two with
  case ^Tuple2 first second then first + second
  case ^((x -> x) Tuple2) any any then "same as above"
  default "nothing matched."
```

## Modules and definitions

A Furipota file is a module, and modules have a different grammar than expressions do. A module must start with a language version identifier, and may only contain declarations at its top level:

```ruby
%furipota/1

define hello = "world"
```

> Note that comments are valid before the version identifier, thus:
>
>     #!/usr/bin/env furipota-run
>     %furipota/1
>     import core "prelude"
{'fraction_non_alphanumeric': 0.10814617152175525, 'fraction_numerical': 0.010822029501148914, 'mean_word_length': 4.272372020320438, 'pattern_counts': {'":': 4, '<': 10, '<?xml version=': 0, '>': 52, 'https://': 8, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '630567', 'n_tokens_mistral': 4108, 'n_tokens_neox': 3776, 'n_words': 1892}