Tacit programming: Difference between revisions

Jump to navigation Jump to search
m
Fix code
No edit summary
m (Fix code)
 
(71 intermediate revisions by 11 users not shown)
Line 1: Line 1:
Tacit functions apply to implicit arguments following a small set of rules. This is in contrast to the explicit use of arguments in [[dfns]] (<source inline lang=apl>⍺ ⍵</source>) and [[tradfns]] (which have named arguments). Known dialects which implement trains are Dyalog APL, dzaima/apl, ngn/apl and NARS2000.
[[File:Function compositions.png|thumb|right|Diagrams of [[function composition]]s, an important part of tacit programming.]]
'''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 (<syntaxhighlight inline lang=apl>⍺ ⍵</syntaxhighlight>) 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]].


== Primitives ==
== Primitives ==
All primitive functions are tacit. Some APLs allow primitive functions to be named.
All [[primitive functions]] are tacit. Some APLs allow primitive functions to be named.
<source lang=apl>
<syntaxhighlight lang=apl>
       plus ← +
       plus ← +
       times ← ×
       times ← ×
       6 times 3 plus 5
       6 times 3 plus 5
48
48
</source>
</syntaxhighlight>
 
== Derived functions ==
Functions derived from a monadic operator and an operand, or from a dyadic operator and two operands are tacit functions:
<syntaxhighlight lang=apl>
      Sum ← +/
      Sum ⍳10
55
 
      Dot ← +.×
      3 1 4 Dot 2 7 1
17
</syntaxhighlight>
 
== Derived operators ==
A dyadic operator with its right operand forms a tacit monadic operator:
<syntaxhighlight lang=apl>
      1(+⍣2)10
12
      Twice ← ⍣2
      1 +Twice 10
12
</syntaxhighlight>


== Trains ==
== Trains ==
A train is a series of functions in isolation. An isolated function is either surrounded by parentheses or named. Arguments are processed by the following rules:
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, called [[fork]]s:
{|
|<syntaxhighlight lang=apl>  (f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>(  f ⍵) g (  h ⍵)</syntaxhighlight>
|-
|<syntaxhighlight lang=apl>⍺ (f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>(⍺ f ⍵) g (⍺ h ⍵)</syntaxhighlight>
|}
The ''left tine'' of a fork can be an array:
{|
|<syntaxhighlight lang=apl>  (A g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>A g (  h ⍵)</syntaxhighlight>
|-
|<syntaxhighlight lang=apl>⍺ (A g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>A g (⍺ h ⍵)</syntaxhighlight>
|}
 
In APL (but not [[J]]), these rules are used for 2-trains, called [[atop]]s:
{|
|<syntaxhighlight lang=apl>  (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (  h ⍵)</syntaxhighlight>
|-
|<syntaxhighlight lang=apl>⍺ (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (⍺ h ⍵)</syntaxhighlight>
|}
 
Any train can be expressed in terms of [[function composition]] &mdash; except dyadic forks. Some common patterns are:
 
{|
|<syntaxhighlight lang=apl>(f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g⍨∘f⍨∘h⍨ ⍵</syntaxhighlight>
|-
|<syntaxhighlight lang=apl>(f g f) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g⍥f⍨ ⍵</syntaxhighlight>
|-
|<syntaxhighlight lang=apl>(⊢ g f) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g∘f⍨ ⍵</syntaxhighlight>
|}
 
== Debugging ==
In [[Dyalog APL]], analysis of trains is assisted by a [[user command]] <syntaxhighlight lang=apl inline>]Boxing on</syntaxhighlight>. This is achieved by executing the command <syntaxhighlight lang=apl inline>]Boxing on</syntaxhighlight> 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:
<syntaxhighlight lang=apl>
      ]Boxing on
Was OFF
      ((+.×⍨⊢~∘.×⍨)1↓⍳)    ⍝ the train to be analysed
┌───────────────────────────────┬───────┐
│┌───────────┬─────────────────┐│┌─┬─┬─┐│
││┌───────┬─┐│┌─┬─┬───────────┐│││1│↓│⍳││
│││┌─┬─┬─┐│⍨│││⊢│~│┌───────┬─┐│││└─┴─┴─┘│
││││+│.│×││ │││ │ ││┌─┬─┬─┐│⍨││││      │
│││└─┴─┴─┘│ │││ │ │││∘│.│×││ ││││      │
││└───────┴─┘││ │ ││└─┴─┴─┘│ ││││      │
││          ││ │ │└───────┴─┘│││      │
││          │└─┴─┴───────────┘││      │
│└───────────┴─────────────────┘│      │
└───────────────────────────────┴───────┘
</syntaxhighlight>
 
Alternatively, a train can be represented in form of a tree:
<syntaxhighlight lang=apl>
      ]Boxing on -trains=tree
Was ON -trains=box
      ((+.×⍨⊢~∘.×⍨)1↓⍳)    ⍝ the train to be analysed
    ┌───┴───┐ 
  ┌─┴─┐  ┌─┼─┐
  ⍨ ┌─┼─┐ 1 ↓ ⍳
┌─┘ ⊢ ~ ⍨     
.    ┌─┘     
┌┴┐    .       
+ ×  ┌┴┐     
      ∘ ×     
</syntaxhighlight>
Or fully parenthesised:
<syntaxhighlight lang=apl>
      ]Boxing on -trains=parens
Was OFF -trains=box
      ((+.×⍨⊢~∘.×⍨)1↓⍳)    ⍝ the train to be analysed
(((+.×)⍨)(⊢~((∘.×)⍨)))(1↓⍳)
</syntaxhighlight>
 
=== 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 <syntaxhighlight lang=apl inline>+∘1</syntaxhighlight>) can indicate a monadic function, and in some contexts, <syntaxhighlight lang=apl inline>=</syntaxhighlight>, which can only be used dyadically, would indicate a dyadic function. The website [https://tacit.help 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 [[idiom]]s.
 
=== Plus and minus ===
<syntaxhighlight lang=apl>
      (+,-) 2    ⍝ ±2
2 ¯2
      5 (+,-) 2  ⍝ 5±2
7 3
</syntaxhighlight>
 
=== Arithmetic mean ===
<syntaxhighlight lang=apl>
      (+⌿÷≢) ⍳10      ⍝ Mean of the first ten integers
5.5
      (+⌿÷≢) 5 4⍴⍳4    ⍝ Mean of columns in a matrix
1 2 3 4
</syntaxhighlight>
 
=== Fractions ===
We can convert decimal numbers to fractions. For example, we can convert <math>2.625</math> to the improper fraction <math>\tfrac{21}{8}</math> with
<syntaxhighlight lang=apl>
      (1∧⊢,÷)2.625
21 8
</syntaxhighlight>
Alternatively, we can convert it to the mixed fraction <math>2\tfrac{5}{8}</math> with a mixed fraction:
<syntaxhighlight lang=apl>
      (1∧0 1∘⊤,÷)2.625
2 5 8
</syntaxhighlight>
 
=== Is it a palindrome? ===
<syntaxhighlight lang=apl>
      (⌽≡⊢)'racecar'
1
      (⌽≡⊢)'racecat'
0
</syntaxhighlight>
 
=== Split delimited text ===
<syntaxhighlight lang=apl>
      ','(≠⊆⊢)'comma,delimited,text'
┌─────┬─────────┬────┐
│comma│delimited│text│
└─────┴─────────┴────┘
      ' '(≠⊆⊢)'space delimited text'
┌─────┬─────────┬────┐
│space│delimited│text│
└─────┴─────────┴────┘
</syntaxhighlight>
 
=== 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 <math>\textbf{a}</math> in the direction of another vector <math>\textbf{b}</math>:
:::<math>\textbf{a}_\textbf{b} = (\textbf{a}\cdot\hat{\textbf{b}})\hat{\textbf{b}}</math>
<syntaxhighlight lang=apl>
      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
</syntaxhighlight>
For a more parallel comparison of the notations, see the [[Comparison_with_traditional_mathematics#Practical_example|comparison with traditional mathematics]].
 
===The Number of the Beast===
The following expression for computing the [[wikipedia:666 (number)|number of the Beast]] (and of [[I.P. Sharp]]'s APL-based email system, [[666 BOX]]) nicely illustrates how to read a train.
<syntaxhighlight lang=apl>
      ((+.×⍨⊢~∘.×⍨)1↓⍳)17 ⍝ Accursed train
666
</syntaxhighlight>
First, <syntaxhighlight lang=apl inline>((+.×⍨⊢~∘.×)1↓⍳)</syntaxhighlight> is supplied with only one argument <syntaxhighlight lang=apl inline>17</syntaxhighlight> and is thus interpreted monadically.
 
Second, <syntaxhighlight lang=apl inline>(+.×⍨⊢~∘.×⍨)1↓⍳</syntaxhighlight> is a 4-train: reading right-to-left, the last 3 components are interpreted as the fork <syntaxhighlight lang=apl inline>1↓⍳</syntaxhighlight> and the 4-train is interpreted as the atop <syntaxhighlight lang=apl inline>(+.×⍨⊢~∘.×⍨)(1↓⍳)</syntaxhighlight>.
Similarly, <syntaxhighlight lang=apl inline>(+.×⍨⊢~∘.×⍨)</syntaxhighlight> is also a 4-train and interpreted as the atop <syntaxhighlight lang=apl inline>+.×⍨(⊢~∘.×⍨)</syntaxhighlight>.
 
Thus the accursed train is interpreted as <syntaxhighlight lang=apl inline>((+.×⍨(⊢~∘.×⍨))(1↓⍳))17</syntaxhighlight>. Having read the train, we now evaluate it monadically.
<syntaxhighlight lang=apl>
      ((+.×⍨(⊢~∘.×⍨))(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
</syntaxhighlight>
Note that <syntaxhighlight lang=apl inline>((⊢~∘.×⍨)1↓⍳)</syntaxhighlight> is a train computing primes up to the given input.
 
A more satisfying variation of the accursed train is the following.
<syntaxhighlight lang=apl>
      (⍎⊢,⍕∘≢)'((+.×⍨⊢~∘.×⍨)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
</syntaxhighlight>


A 2-train is an ''atop'':
== External links ==
<source lang=apl>
=== Tutorials ===
  (g h) ⍵ ⬄ g (  h ⍵)
<div style="column-width:30em">
⍺ (g h) ⍵ ⬄ g (⍺ h ⍵)
==== In text form ====
</source>
* [[Learning APL]]: [https://xpqz.github.io/learnapl/tacit.html Trainspotting]
* [[Documentation_suites#Dyalog_APL|Dyalog documentation]]: [https://help.dyalog.com/16.0/Content/RelNotes14.0/Function%20Trains.htm version 14.0 release notes]
* [[Dfns workspace]]: [https://dfns.dyalog.com/n_tacit.htm Translation of <nowiki>[dfns]</nowiki> into tacit form]
* [[Dfns workspace]]: [https://dfns.dyalog.com/n_dft.htm Display of function tree]
* [[Dfns workspace]]: [https://dfns.dyalog.com/n_fork.htm Simulation of fork syntax]
* [[APL Cultivation]]: [https://chat.stackexchange.com/rooms/52405/conversation/lesson-23-transcribing-to-and-reading-trains Transcribing to and reading trains]
* gitonthescene: [https://gist.github.com/gitonthescene/666c77ee3ed0ae0a79cf8e057584b7fd Forks: Spoon fed]
* gitonthescene: [https://gist.github.com/gitonthescene/5e9c25ab9edd2f2ce0d5ad38d8a8b2b4 Training day]


A 3-train is a ''fork'':
<source lang=apl>
  (f g h) ⍵ ⬄ (  f ⍵) g (  h ⍵)
⍺ (f g h) ⍵ ⬄ (⍺ f ⍵) g (⍺ h ⍵)
</source>


The ''left tine'' of a fork (but not an atop) can be an array:
==== Videos ====
<source lang=apl>
* [[APLtrainer]]: [https://www.youtube.com/watch?v=kt4lMZbn-so How to read trains in Dyalog APL code]
  (A g h) ⍵ ⬄ A g (  h ⍵)
* [[APLtrainer]]: [https://www.youtube.com/watch?v=A2LqqBosvY0 Function trains in APL]
⍺ (A g h) ⍵ ⬄ A g (⍺ h ⍵)
* [[Dyalog webinar]]: [https://www.youtube.com/watch?v=Enlh5qwwDuY?t=440 Train Spotting in Dyalog APL]
</source>
* [[Dyalog '13]]: [https://www.youtube.com/watch?v=7-93GzDqC08 Train Spotting in Version 14.0]
</div>


== Expressing algorithms ==
== References ==
One of the major benefits of tacit programming is the ability to convey a short, well-defined idea as an isolated expression ([https://aplwiki.com/wiki/Simple_examples#Tacit_programming example]).
<references/>


{{APL Language}}
{{APL syntax}}[[Category:Tacit programming| ]]
1

edit

Navigation menu