# Tacit programming: Difference between revisions

(Move some material to a dedicated page on trains) |
|||

Line 1: | Line 1: | ||

'''Tacit programming''', also called '''[[wikipedia:Tacit_programming|point-free style]]''', refers to usage of tacit [[function]]s that are defined in terms of implicit [[argument]]s. This is in contrast to the explicit use of arguments in [[dfn]]s (<source inline lang=apl>⍺ ⍵</source>) and [[tradfn]]s (which have named arguments). Some APL dialects allow to combine functions into [[ | '''Tacit programming''', also called '''[[wikipedia:Tacit_programming|point-free style]]''', refers to usage of tacit [[function]]s that are defined in terms of implicit [[argument]]s. This is in contrast to the explicit use of arguments in [[dfn]]s (<source inline lang=apl>⍺ ⍵</source>) and [[tradfn]]s (which have named arguments). Some APL dialects allow to combine functions into [[train]]s following a small set of rules. This allows creating complex [[derived function]]s without specifying any arguments explicitly. | ||

Dialects which implement trains include [[Dyalog APL]], [[dzaima/APL]], [[ngn/apl]] and [[NARS2000]]. | Dialects which implement trains include [[Dyalog APL]], [[dzaima/APL]], [[ngn/apl]] and [[NARS2000]]. | ||

Line 34: | Line 34: | ||

== Trains == | == Trains == | ||

A train is a series of functions in isolation. An isolated function is either surrounded by parentheses or named. | A [[train]] is a series of functions in isolation. An isolated function is either surrounded by parentheses or named. | ||

These rules are used for 3-trains: | |||

{| | {| | ||

|<source lang=apl> (f g h) ⍵</source>|| {{←→}} ||<source lang=apl>( f ⍵) g ( h ⍵)</source> | |<source lang=apl> (f g h) ⍵</source>|| {{←→}} ||<source lang=apl>( f ⍵) g ( h ⍵)</source> | ||

Line 50: | Line 49: | ||

|} | |} | ||

In APL (but not [[J]]), these rules are used for 2-trains: | |||

{| | {| | ||

|<source lang=apl> (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g ( h ⍵)</source> | |<source lang=apl> (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g ( h ⍵)</source> | ||

Line 57: | Line 55: | ||

|<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g (⍺ h ⍵)</source> | |<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g (⍺ h ⍵)</source> | ||

|} | |} | ||

== Debugging == | == Debugging == | ||

Line 237: | Line 219: | ||

* [[Dyalog '13]]: [https://www.youtube.com/watch?v=7-93GzDqC08 Train Spotting in Version 14.0] | * [[Dyalog '13]]: [https://www.youtube.com/watch?v=7-93GzDqC08 Train Spotting in Version 14.0] | ||

</div> | </div> | ||

== References == | == References == |

## Revision as of 15:31, 8 August 2022

**Tacit programming**, also called **point-free style**, refers to usage of tacit functions that are defined in terms of implicit arguments. This is in contrast to the explicit use of arguments in dfns (`⍺ ⍵`

) and tradfns (which have named arguments). Some APL dialects allow to combine functions into trains following a small set of rules. This allows creating complex derived functions without specifying any arguments explicitly.

Dialects which implement trains include Dyalog APL, dzaima/APL, ngn/apl and NARS2000.

## Primitives

All primitive functions are tacit. Some APLs allow primitive functions to be named.

```
plus ← +
times ← ×
6 times 3 plus 5
48
```

## Derived functions

Functions derived from a monadic operator and an operand, or from a dyadic operator and two operands are tacit functions:

```
Sum ← +/
Sum ⍳10
55
Dot ← +.×
3 1 4 dot 2 7 1
17
```

## Derived operators

A dyadic operator with its right operand forms a tacit monadic operator:

```
1(+⍣2)10
12
Twice ← ⍣2
1 +Twice 10
12
```

## Trains

A train is a series of functions in isolation. An isolated function is either surrounded by parentheses or named.

These rules are used for 3-trains:

```
(f g h) ⍵
``` |
```
( f ⍵) g ( h ⍵)
``` | |

```
⍺ (f g h) ⍵
``` |
```
(⍺ f ⍵) g (⍺ h ⍵)
``` |

The *left tine* of a fork can be an array:

```
(A g h) ⍵
``` |
```
A g ( h ⍵)
``` | |

```
⍺ (A g h) ⍵
``` |
```
A g (⍺ h ⍵)
``` |

In APL (but not J), these rules are used for 2-trains:

```
(g h) ⍵
``` |
```
g ( h ⍵)
``` | |

```
⍺ (g h) ⍵
``` |
```
g (⍺ h ⍵)
``` |

## Debugging

In Dyalog APL, analysis of trains is assisted by a user command `]Boxing on`

. This is achieved by executing the command `]Boxing on`

and then entering a train without any parameters. A structure of the train will be displayed.

For example, the "accursed train" from the section below can be analysed like this:

```
]Boxing on
Was OFF
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed
┌───────────────────────────────┬───────┐
│┌───────────┬─────────────────┐│┌─┬─┬─┐│
││┌───────┬─┐│┌─┬─┬───────────┐│││1│↓│⍳││
│││┌─┬─┬─┐│⍨│││⊢│~│┌───────┬─┐│││└─┴─┴─┘│
││││+│.│×││ │││ │ ││┌─┬─┬─┐│⍨││││ │
│││└─┴─┴─┘│ │││ │ │││∘│.│×││ ││││ │
││└───────┴─┘││ │ ││└─┴─┴─┘│ ││││ │
││ ││ │ │└───────┴─┘│││ │
││ │└─┴─┴───────────┘││ │
│└───────────┴─────────────────┘│ │
└───────────────────────────────┴───────┘
```

Alternatively, a train can be represented in form of a tree:

```
]Boxing on -trains=tree
Was ON -trains=box
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed
┌───┴───┐
┌─┴─┐ ┌─┼─┐
⍨ ┌─┼─┐ 1 ↓ ⍳
┌─┘ ⊢ ~ ⍨
. ┌─┘
┌┴┐ .
+ × ┌┴┐
∘ ×
```

Or fully parenthesised:

```
]Boxing on -trains=parens
Was OFF -trains=box
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed
(((+.×)⍨)(⊢~((∘.×)⍨)))(1↓⍳)
```

### Conversion to dfns

It can help understanding to convert a tacit function to a dfn. For many tacit functions, it is not immediately clear if the intention of the function is to be used monadically or dyadically, or even both. Such knowledge can be conveyed by comments, but sometimes it is possible to spot patterns that are exclusively monadic or dyadic: A function with a bound argument (for example `+∘1`

) can indicate a monadic function, and in some contexts, `=`

, which can only be used dyadically, would indicate a monadic function. The website tacit.help provides automated translation of most tacit functions, into both monadic and dyadic, fully parenthesised dfns.

## Examples

One of the major benefits of tacit programming is the ability to convey a short, well-defined idea as an isolated expression. This aids both human readability (semantic density) and the computer's ability to interpret code, potentially executing special code for particular idioms.

### Plus and minus

```
(+,-) 2 ⍝ ±2
2 ¯2
5 (+,-) 2 ⍝ 5±2
7 3
```

### Arithmetic mean

```
(+⌿÷≢) ⍳10 ⍝ Mean of the first ten integers
5.5
(+⌿÷≢) 5 4⍴⍳4 ⍝ Mean of columns in a matrix
1 2 3 4
```

### Fractions

We can convert decimal numbers to fractions. For example, we can convert to the improper fraction with

```
(1∧⊢,÷)2.625
21 8
```

Alternatively, we can convert it to the mixed fraction with a mixed fraction:

```
(1∧0 1∘⊤,÷)2.625
2 5 8
```

### Is it a palindrome?

```
(⌽≡⊢)'racecar'
1
(⌽≡⊢)'racecat'
0
```

### Split delimited text

```
','(≠⊆⊢)'comma,delimited,text'
┌─────┬─────────┬────┐
│comma│delimited│text│
└─────┴─────────┴────┘
' '(≠⊆⊢)'space delimited text'
┌─────┬─────────┬────┐
│space│delimited│text│
└─────┴─────────┴────┘
```

### Component of a vector in the direction of another vector

Sometimes a train can make an expression nicely resemble its equivalent definition in traditional mathematical notation. As an example, here is a program to compute the component of a vector in the direction of another vector :

```
Root ← *∘÷⍨ ⍝ Nth root
Norm ← 2 Root +.×⍨ ⍝ Magnitude (norm) of numeric vector in Euclidean space
Unit ← ⊢÷Norm ⍝ Unit vector in direction of vector ⍵
InDirOf ← (⊢×+.×)∘Unit ⍝ Component of vector ⍺ in direction of vector ⍵
3 5 2 InDirOf 0 0 1 ⍝ Trivial example
0 0 2
```

For a more parallel comparison of the notations, see the comparison with traditional mathematics.

### The Number of the Beast

The following expression for computing the number of the Beast (and of I.P. Sharp's APL-based email system, 666 BOX) nicely illustrates how to read a train.

```
((+.×⍨⊢~∘.×⍨)1↓⍳)17 ⍝ Accursed train
666
```

First, `((+.×⍨⊢~∘.×)1↓⍳)`

is supplied with only one argument `17`

and is thus interpreted monadically.

Second, `(+.×⍨⊢~∘.×⍨)1↓⍳`

is a 4-train: reading right-to-left, the last 3 components are interpreted as the fork `1↓⍳`

and the 4-train is interpreted as the atop `(+.×⍨⊢~∘.×⍨)(1↓⍳)`

.
Similarly, `(+.×⍨⊢~∘.×⍨)`

is also a 4-train and interpreted as the atop `+.×⍨(⊢~∘.×⍨)`

.

Thus the accursed train is interpreted as `((+.×⍨(⊢~∘.×⍨))(1↓⍳))17`

. Having read the train, we now evaluate it monadically.

```
((+.×⍨(⊢~∘.×⍨))(1↓⍳))17 ⍝ Accursed train as an atop over a fork atop a fork
+.×⍨(⊢~∘.×⍨)1↓⍳17 ⍝ Atop evalution
+.×⍨(⊢1↓⍳17)~∘.×⍨1↓⍳17 ⍝ Fork evalution
+.×⍨(1↓⍳17)~∘.×⍨1↓⍳17 ⍝ ⊢ evaluation
+.×⍨2 3 5 7 11 13 17 ⍝ numbers 2 through 17 without those appearing in their multiplication table are primes
666 ⍝ the sum of the squares of the primes up to 17
```

Note that `((⊢⍨∘.×⍨)1↓⍳)`

is a train computing primes up to the given input.

A more satisfying variation of the accursed train is the following.

```
(⍎⊢,⍕∘≢)'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ Accursed train 2.0
⍎(⊢,⍕∘≢)'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ 4-train intepreted as an atop
⍎(⊢'((+.×⍨⊢~∘.×⍨)1↓⍳)'),⍕∘≢'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ fork evaluation
⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)','17' ⍝ ⊢ evaluation and ⍕∘≢ evaluation
⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)17' ⍝ , evaluation
666 ⍝ ⍎ executes original Accursed train
```

## See also

## External links

### Tutorials

#### In text form

- Learning APL: Trainspotting
- Dyalog documentation: version 14.0 release notes
- Dfns workspace: Translation of [dfns] into tacit form
- APL Cultivation: Transcribing to and reading trains
- gitonthescene: Forks: Spoon fed
- gitonthescene: Training day

#### Videos

## References