https://aplwiki.com/api.php?action=feedcontributions&user=Dehond&feedformat=atomAPL Wiki - User contributions [en-gb]2024-03-29T06:13:48ZUser contributionsMediaWiki 1.38.2https://aplwiki.com/index.php?title=Tacit_programming&diff=8311Tacit programming2022-04-09T15:19:17Z<p>Dehond: 15 isn't prime, and shouldn't be included here.</p>
<hr />
<div>'''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 [[#trains|trains]] following a small set of rules. This allows creating complex [[derived function]]s without specifying any arguments explicitly.<br />
<br />
Dialects which implement trains include [[Dyalog APL]], [[dzaima/APL]], [[ngn/apl]] and [[NARS2000]].<br />
<br />
== Primitives ==<br />
All [[primitive functions]] are tacit. Some APLs allow primitive functions to be named.<br />
<source lang=apl><br />
plus ← +<br />
times ← ×<br />
6 times 3 plus 5<br />
48<br />
</source><br />
<br />
== Derived functions ==<br />
Functions derived from a monadic operator and an operand, or from a dyadic operator and two operands are tacit functions:<br />
<source lang=apl><br />
Sum ← +/<br />
Sum ⍳10<br />
55<br />
<br />
Dot ← +.×<br />
3 1 4 dot 2 7 1<br />
17<br />
</source><br />
== Derived operators ==<br />
A dyadic operator with its right operand forms a tacit monadic operator:<br />
<source lang=apl><br />
1(+⍣2)10<br />
12<br />
Twice ← ⍣2<br />
1 +Twice 10<br />
12<br />
</source><br />
<br />
== Trains ==<br />
A train is a series of functions in isolation. An isolated function is either surrounded by parentheses or named. Below, <source lang=apl inline>⍺</source> and <source lang=apl inline>⍵</source> refer to the arguments of the train. <source lang=apl inline>f</source>, <source lang=apl inline>g</source>, and <source lang=apl inline>h</source> are functions (which themselves can be tacit or not), and <source lang=apl inline>A</source> is an array. The arguments are processed by the following rules:<br />
<br />
=== 3-trains ===<br />
A 3-train is a ''fork'', so denoted because its structure resembles a three-tines fork, or a three-pronged pitchfork. The two outer functions are applied first, and their results are used as arguments to the middle function:<br />
{|<br />
|<source lang=apl> (f g h) ⍵</source>|| {{←→}} ||<source lang=apl>( f ⍵) g ( h ⍵)</source><br />
|-<br />
|<source lang=apl>⍺ (f g h) ⍵</source>|| {{←→}} ||<source lang=apl>(⍺ f ⍵) g (⍺ h ⍵)</source><br />
|}<br />
The ''left tine'' of a fork can be an array:<br />
{|<br />
|<source lang=apl> (A g h) ⍵</source>|| {{←→}} ||<source lang=apl>A g ( h ⍵)</source><br />
|-<br />
|<source lang=apl>⍺ (A g h) ⍵</source>|| {{←→}} ||<source lang=apl>A g (⍺ h ⍵)</source><br />
|}<br />
<br />
=== 2-trains ===<br />
Most dialects define a 2-train is an ''atop'', equivalent to the function derived using the [[Atop (operator)|Atop]] operator. The left function is applied [[monadic function|monadically]] on the result of the right function:<br />
{|<br />
|<source lang=apl> (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g ( h ⍵)</source><br />
|-<br />
|<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g (⍺ h ⍵)</source><br />
|}<br />
<br />
Only [[dzaima/APL]] allows <source lang=apl inline>(A h)</source>, which it treats as <source lang=apl inline>A∘h</source>.<ref>dzaima/APL: [https://github.com/dzaima/APL/blob/ceea05e25687988ed0980a4abf4b9249b736543f/docs/differences.txt#L19 Differences from Dyalog APL]. Retrieved 09 Jan 2020.</ref> See [[Bind]].<br />
<br />
[[J]] instead defines the 2-train as a [[hook]], equivalent to the function derived using the [[Withe]] operator. The left function is always applied [[dyadic function|dyadically]], taking as right argument, the result of applying the right function on the right argument. If there is no left argument, the sole argument is used also as left argument:<br />
{|<br />
|<source lang=apl> (g h) ⍵</source>|| {{←→}} ||<source lang=apl>⍵ g (h ⍵)</source><br />
|-<br />
|<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>⍺ g (h ⍵)</source><br />
|}<br />
=== Problems caused by function-operator overloading ===<br />
Trains that use a [[Function-operator_overloading|hybrid function-operator]] in its [[function]] role can run into the problems with the hybrid being parsed as a monadic [[operator]] instead of as a function. This happens when a function appears to the immediate left of the hybrid, causing this function to be bound as the hybrid's operand — the hybrid taking on an operator role — rather than supplying a left [[argument]] or post-processing the result.<br />
<br />
For example, the attempted [[#3-trains|fork]] <source lang=apl inline>f/h</source> is actually parsed as the [[#2-trains|atop]] <source lang=apl inline>(f/)h</source> and the attempted atop <source lang=apl inline>f/</source> is actually parsed as a [[Windowed Reduce|Windowed Reduction]]. There are multiple [[Function-operator_overloading#Mitigation|ways to mitigate this issue]]. For example, the fork can be enforced using the [[Atop (operator)|Atop operator]] by applying identity to the hybrid's result as <source lang=apl inline>f⊢⍤/h</source> and the atop can be enforced by using the explicit Atop operator instead of a 2-train; <source lang=apl inline>f⍤/</source>.<br />
<br />
No problem presents when left argument is supplied as an array (literal or by name reference) and when the hybrid is the leftmost token. For example, <source lang=apl inline>1 0 1/⌽</source> and <source lang=apl inline>/,⊃</source> are parsed as forks.<br />
<br />
== Debugging ==<br />
In [[Dyalog APL]], analysis of trains is assisted by a [[user command]] <source lang=apl inline>]Boxing on</source>. This is achieved by executing the command <source lang=apl inline>]Boxing on</source> and then entering a train without any parameters. A structure of the train will be displayed.<br />
<br />
For example, the "accursed train" from the section below can be analysed like this:<br />
<source lang=apl><br />
]Boxing on<br />
Was OFF<br />
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed<br />
┌───────────────────────────────┬───────┐<br />
│┌───────────┬─────────────────┐│┌─┬─┬─┐│<br />
││┌───────┬─┐│┌─┬─┬───────────┐│││1│↓│⍳││<br />
│││┌─┬─┬─┐│⍨│││⊢│~│┌───────┬─┐│││└─┴─┴─┘│<br />
││││+│.│×││ │││ │ ││┌─┬─┬─┐│⍨││││ │<br />
│││└─┴─┴─┘│ │││ │ │││∘│.│×││ ││││ │<br />
││└───────┴─┘││ │ ││└─┴─┴─┘│ ││││ │<br />
││ ││ │ │└───────┴─┘│││ │<br />
││ │└─┴─┴───────────┘││ │<br />
│└───────────┴─────────────────┘│ │<br />
└───────────────────────────────┴───────┘<br />
</source><br />
<br />
Alternatively, a train can be represented in form of a tree:<br />
<source lang=apl><br />
]Boxing on -trains=tree<br />
Was ON -trains=box<br />
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed<br />
┌───┴───┐ <br />
┌─┴─┐ ┌─┼─┐<br />
⍨ ┌─┼─┐ 1 ↓ ⍳<br />
┌─┘ ⊢ ~ ⍨ <br />
. ┌─┘ <br />
┌┴┐ . <br />
+ × ┌┴┐ <br />
∘ × <br />
</source><br />
Or fully parenthesised:<br />
<source lang=apl><br />
]Boxing on -trains=parens<br />
Was OFF -trains=box<br />
((+.×⍨⊢~∘.×⍨)1↓⍳) ⍝ the train to be analysed<br />
(((+.×)⍨)(⊢~((∘.×)⍨)))(1↓⍳)<br />
</source><br />
<br />
== Examples ==<br />
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.<br />
<br />
=== Plus and minus ===<br />
<source lang=apl><br />
(+,-) 2 ⍝ ±2<br />
2 ¯2<br />
5 (+,-) 2 ⍝ 5±2<br />
7 3<br />
</source><br />
<br />
=== Arithmetic mean ===<br />
<source lang=apl><br />
(+⌿÷≢) ⍳10 ⍝ Mean of the first ten integers<br />
5.5<br />
(+⌿÷≢) 5 4⍴⍳4 ⍝ Mean of columns in a matrix<br />
1 2 3 4<br />
</source><br />
<br />
=== Fractions ===<br />
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<br />
<source lang=apl><br />
(1∧⊢,÷)2.625<br />
21 8<br />
</source><br />
Alternatively, we can convert it to the mixed fraction <math>2\tfrac{5}{8}</math> with a mixed fraction:<br />
<source lang=apl><br />
(1∧0 1∘⊤,÷)2.625<br />
2 5 8<br />
</source><br />
<br />
=== Is it a palindrome? ===<br />
<source lang=apl><br />
(⌽≡⊢)'racecar'<br />
1<br />
(⌽≡⊢)'racecat'<br />
0<br />
</source><br />
<br />
=== Split delimited text ===<br />
<source lang=apl><br />
','(≠⊆⊢)'comma,delimited,text'<br />
┌─────┬─────────┬────┐<br />
│comma│delimited│text│<br />
└─────┴─────────┴────┘<br />
' '(≠⊆⊢)'space delimited text'<br />
┌─────┬─────────┬────┐<br />
│space│delimited│text│<br />
└─────┴─────────┴────┘<br />
</source><br />
<br />
=== Component of a vector in the direction of another vector ===<br />
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>:<br />
:::<math>\textbf{a}_\textbf{b} = (\textbf{a}\cdot\hat{\textbf{b}})\hat{\textbf{b}}</math><br />
<source lang=apl><br />
Root ← *∘÷⍨ ⍝ Nth root<br />
Norm ← 2 Root +.×⍨ ⍝ Magnitude (norm) of numeric vector in Euclidean space<br />
Unit ← ⊢÷Norm ⍝ Unit vector in direction of vector ⍵<br />
InDirOf ← (⊢×+.×)∘Unit ⍝ Component of vector ⍺ in direction of vector ⍵<br />
3 5 2 InDirOf 0 0 1 ⍝ Trivial example<br />
0 0 2<br />
</source><br />
For a more parallel comparison of the notations, see the [[Comparison_with_traditional_mathematics#Practical_example|comparison with traditional mathematics]].<br />
<br />
===The Number of the Beast===<br />
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.<br />
<source lang=apl><br />
((+.×⍨⊢~∘.×⍨)1↓⍳)17 ⍝ Accursed train<br />
666<br />
</source><br />
First, <source lang=apl inline>((+.×⍨⊢~∘.×)1↓⍳)</source> is supplied with only one argument <source lang=apl inline>17</source> and is thus interpreted monadically.<br />
<br />
Second, <source lang=apl inline>(+.×⍨⊢~∘.×⍨)1↓⍳</source> is a 4-train: reading right-to-left, the last 3 components are interpreted as the fork <source lang=apl inline>1↓⍳</source> and the 4-train is interpreted as the atop <source lang=apl inline>(+.×⍨⊢~∘.×⍨)(1↓⍳)</source>.<br />
Similarly, <source lang=apl inline>(+.×⍨⊢~∘.×⍨)</source> is also a 4-train and interpreted as the atop <source lang=apl inline>+.×⍨(⊢~∘.×⍨)</source>. <br />
<br />
Thus the accursed train is interpreted as <source lang=apl inline>((+.×⍨(⊢~∘.×⍨))(1↓⍳))17</source>. Having read the train, we now evaluate it monadically.<br />
<source lang=apl><br />
((+.×⍨(⊢~∘.×⍨))(1↓⍳))17 ⍝ Accursed train as an atop over a fork atop a fork<br />
+.×⍨(⊢~∘.×⍨)1↓⍳17 ⍝ Atop evalution<br />
+.×⍨(⊢1↓⍳17)~∘.×⍨1↓⍳17 ⍝ Fork evalution<br />
+.×⍨(1↓⍳17)~∘.×⍨1↓⍳17 ⍝ ⊢ evaluation<br />
+.×⍨2 3 5 7 11 13 17 ⍝ numbers 2 through 17 without those appearing in their multiplication table are primes<br />
666 ⍝ the sum of the squares of the primes up to 17<br />
</source><br />
Note that <source lang=apl inline>((⊢⍨∘.×⍨)1↓⍳)</source> is a train computing primes up to the given input.<br />
<br />
A more satisfying variation of the accursed train is the following.<br />
<source lang=apl><br />
(⍎⊢,⍕∘≢)'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ Accursed train 2.0<br />
⍎(⊢,⍕∘≢)'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ 4-train intepreted as an atop<br />
⍎(⊢'((+.×⍨⊢~∘.×⍨)1↓⍳)'),⍕∘≢'((+.×⍨⊢~∘.×⍨)1↓⍳)' ⍝ fork evaluation<br />
⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)','17' ⍝ ⊢ evaluation and ⍕∘≢ evaluation<br />
⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)17' ⍝ , evaluation<br />
666 ⍝ ⍎ executes original Accursed train<br />
</source><br />
<br />
== See also ==<br />
<br />
* [[Function composition]]<br />
<br />
== External links ==<br />
=== Tutorials ===<br />
* [[Learning APL]]: [https://xpqz.github.io/learnapl/tacit.html Trainspotting]<br />
* [[Documentation_suites#Dyalog_APL|Dyalog documentation]]: [https://help.dyalog.com/16.0/Content/RelNotes14.0/Function%20Trains.htm version 14.0 release notes]<br />
* [[Dfns workspace]]: [https://dfns.dyalog.com/n_tacit.htm Translation of <nowiki>[dfns]</nowiki> into tacit form]<br />
* [[APL Cultivation]]: [https://chat.stackexchange.com/rooms/52405/conversation/lesson-23-transcribing-to-and-reading-trains Transcribing to and reading trains]<br />
* [[APLtrainer]]: [https://www.youtube.com/watch?v=kt4lMZbn-so How to read trains in Dyalog APL code] (video)<br />
* [[APLtrainer]]: [https://www.youtube.com/watch?v=A2LqqBosvY0 Function trains in APL] (video)<br />
* [[Dyalog webinar]]: [https://www.youtube.com/watch?v=Enlh5qwwDuY?t=440 Train Spotting in Dyalog APL] (video)<br />
* [[Dyalog '13]]: [https://www.youtube.com/watch?v=7-93GzDqC08 Train Spotting in Version 14.0] (video)<br />
<br />
=== Documentation ===<br />
* [https://help.dyalog.com/16.0/Content/RelNotes14.0/Function%20Trains.htm Announcement]<br />
* [https://help.dyalog.com/latest/Content/Language/Introduction/Trains.htm Dyalog]<br />
<br />
== References ==<br />
<references/><br />
<br />
{{APL syntax}}[[Category:Tacit programming| ]]</div>Dehond