Tacit programming: Difference between revisions

Jump to navigation Jump to search
432 bytes added ,  10:33, 11 September 2022
m
Text replacement - "</source>" to "</syntaxhighlight>"
m (Text replacement - "<source" to "<syntaxhighlight")
m (Text replacement - "</source>" to "</syntaxhighlight>")
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 (<syntaxhighlight 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.
'''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]].
Dialects which implement trains include [[Dyalog APL]], [[dzaima/APL]], [[ngn/apl]] and [[NARS2000]].
Line 10: Line 10:
       6 times 3 plus 5
       6 times 3 plus 5
48
48
</source>
</syntaxhighlight>


== Derived functions ==
== Derived functions ==
Line 22: Line 22:
       3 1 4 dot 2 7 1
       3 1 4 dot 2 7 1
17
17
</source>
</syntaxhighlight>
== Derived operators ==
== Derived operators ==
A dyadic operator with its right operand forms a tacit monadic operator:
A dyadic operator with its right operand forms a tacit monadic operator:
Line 31: Line 31:
       1 +Twice 10
       1 +Twice 10
12
12
</source>
</syntaxhighlight>


== Trains ==
== Trains ==
Line 38: Line 38:
These rules are used for 3-trains:
These rules are used for 3-trains:
{|
{|
|<syntaxhighlight lang=apl>  (f g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>(  f ⍵) g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>(  f ⍵) g (  h ⍵)</syntaxhighlight>
|-
|-
|<syntaxhighlight lang=apl>⍺ (f g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>(⍺ f ⍵) g (⍺ h ⍵)</source>
|<syntaxhighlight lang=apl>⍺ (f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>(⍺ f ⍵) g (⍺ h ⍵)</syntaxhighlight>
|}
|}
The ''left tine'' of a fork can be an array:
The ''left tine'' of a fork can be an array:
{|
{|
|<syntaxhighlight lang=apl>  (A g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>A g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (A g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>A g (  h ⍵)</syntaxhighlight>
|-
|-
|<syntaxhighlight lang=apl>⍺ (A g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>A g (⍺ h ⍵)</source>
|<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:
In APL (but not [[J]]), these rules are used for 2-trains:
{|
{|
|<syntaxhighlight lang=apl>  (g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (  h ⍵)</syntaxhighlight>
|-
|-
|<syntaxhighlight lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>g (⍺ h ⍵)</source>
|<syntaxhighlight lang=apl>⍺ (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (⍺ h ⍵)</syntaxhighlight>
|}
|}


Line 59: Line 59:


{|
{|
|<syntaxhighlight lang=apl>(f g h) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>g⍨∘f⍨∘h⍨ ⍵</source>
|<syntaxhighlight lang=apl>(f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g⍨∘f⍨∘h⍨ ⍵</syntaxhighlight>
|-
|-
|<syntaxhighlight lang=apl>(f g f) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>g⍥f⍨ ⍵</source>
|<syntaxhighlight lang=apl>(f g f) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g⍥f⍨ ⍵</syntaxhighlight>
|-
|-
|<syntaxhighlight lang=apl>(⊢ g f) ⍵</source>|| {{←→}} ||<syntaxhighlight lang=apl>g∘f⍨ ⍵</source>
|<syntaxhighlight lang=apl>(⊢ g f) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g∘f⍨ ⍵</syntaxhighlight>
|}
|}




== Debugging ==
== Debugging ==
In [[Dyalog APL]], analysis of trains is assisted by a [[user command]] <syntaxhighlight lang=apl inline>]Boxing on</source>. This is achieved by executing the command <syntaxhighlight lang=apl inline>]Boxing on</source> and then entering a train without any parameters. A structure of the train will be displayed.
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:
For example, the "accursed train" from the section below can be analysed like this:
Line 86: Line 86:
│└───────────┴─────────────────┘│      │
│└───────────┴─────────────────┘│      │
└───────────────────────────────┴───────┘
└───────────────────────────────┴───────┘
</source>
</syntaxhighlight>


Alternatively, a train can be represented in form of a tree:
Alternatively, a train can be represented in form of a tree:
Line 101: Line 101:
+ ×  ┌┴┐       
+ ×  ┌┴┐       
       ∘ ×       
       ∘ ×       
</source>
</syntaxhighlight>
Or fully parenthesised:
Or fully parenthesised:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
Line 108: Line 108:
       ((+.×⍨⊢~∘.×⍨)1↓⍳)    ⍝ the train to be analysed
       ((+.×⍨⊢~∘.×⍨)1↓⍳)    ⍝ the train to be analysed
(((+.×)⍨)(⊢~((∘.×)⍨)))(1↓⍳)
(((+.×)⍨)(⊢~((∘.×)⍨)))(1↓⍳)
</source>
</syntaxhighlight>


=== Conversion to dfns ===
=== 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</source>) can indicate a monadic function, and in some contexts, <syntaxhighlight lang=apl inline>=</source>, 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.
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 ==
== 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.
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.
Line 121: Line 121:
       5 (+,-) 2  ⍝ 5±2
       5 (+,-) 2  ⍝ 5±2
7 3
7 3
</source>
</syntaxhighlight>


=== Arithmetic mean ===
=== Arithmetic mean ===
Line 129: Line 129:
       (+⌿÷≢) 5 4⍴⍳4    ⍝ Mean of columns in a matrix
       (+⌿÷≢) 5 4⍴⍳4    ⍝ Mean of columns in a matrix
1 2 3 4
1 2 3 4
</source>
</syntaxhighlight>


=== Fractions ===
=== Fractions ===
Line 136: Line 136:
       (1∧⊢,÷)2.625
       (1∧⊢,÷)2.625
21 8
21 8
</source>
</syntaxhighlight>
Alternatively, we can convert it to the mixed fraction <math>2\tfrac{5}{8}</math> with a mixed fraction:
Alternatively, we can convert it to the mixed fraction <math>2\tfrac{5}{8}</math> with a mixed fraction:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
       (1∧0 1∘⊤,÷)2.625
       (1∧0 1∘⊤,÷)2.625
2 5 8
2 5 8
</source>
</syntaxhighlight>


=== Is it a palindrome? ===
=== Is it a palindrome? ===
Line 149: Line 149:
       (⌽≡⊢)'racecat'
       (⌽≡⊢)'racecat'
0
0
</source>
</syntaxhighlight>


=== Split delimited text ===
=== Split delimited text ===
Line 161: Line 161:
│space│delimited│text│
│space│delimited│text│
└─────┴─────────┴────┘
└─────┴─────────┴────┘
</source>
</syntaxhighlight>


=== Component of a vector in the direction of another vector ===
=== Component of a vector in the direction of another vector ===
Line 173: Line 173:
       3 5 2 InDirOf 0 0 1      ⍝ Trivial example
       3 5 2 InDirOf 0 0 1      ⍝ Trivial example
0 0 2
0 0 2
</source>
</syntaxhighlight>
For a more parallel comparison of the notations, see the [[Comparison_with_traditional_mathematics#Practical_example|comparison with traditional mathematics]].
For a more parallel comparison of the notations, see the [[Comparison_with_traditional_mathematics#Practical_example|comparison with traditional mathematics]].


Line 181: Line 181:
       ((+.×⍨⊢~∘.×⍨)1↓⍳)17 ⍝ Accursed train
       ((+.×⍨⊢~∘.×⍨)1↓⍳)17 ⍝ Accursed train
666
666
</source>
</syntaxhighlight>
First, <syntaxhighlight lang=apl inline>((+.×⍨⊢~∘.×)1↓⍳)</source> is supplied with only one argument <syntaxhighlight lang=apl inline>17</source> and is thus interpreted monadically.
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↓⍳</source> is a 4-train: reading right-to-left, the last 3 components are interpreted as the fork <syntaxhighlight lang=apl inline>1↓⍳</source> and the 4-train is interpreted as the atop <syntaxhighlight lang=apl inline>(+.×⍨⊢~∘.×⍨)(1↓⍳)</source>.
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>(+.×⍨⊢~∘.×⍨)</source> is also a 4-train and interpreted as the atop <syntaxhighlight lang=apl inline>+.×⍨(⊢~∘.×⍨)</source>.  
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</source>. Having read the train, we now evaluate it monadically.
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>
<syntaxhighlight lang=apl>
       ((+.×⍨(⊢~∘.×⍨))(1↓⍳))17 ⍝ Accursed train as an atop over a fork atop a fork
       ((+.×⍨(⊢~∘.×⍨))(1↓⍳))17 ⍝ Accursed train as an atop over a fork atop a fork
Line 195: Line 195:
       +.×⍨2 3 5 7 11 13 17    ⍝ numbers 2 through 17 without those appearing in their multiplication table are primes
       +.×⍨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
666                          ⍝ the sum of the squares of the primes up to 17
</source>
</syntaxhighlight>
Note that <syntaxhighlight lang=apl inline>((⊢⍨∘.×⍨)1↓⍳)</source> is a train computing primes up to the given input.
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.
A more satisfying variation of the accursed train is the following.
Line 206: Line 206:
       ⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)17'                        ⍝ , evaluation
       ⍎'((+.×⍨⊢~∘.×⍨)1↓⍳)17'                        ⍝ , evaluation
666                                                  ⍝ ⍎ executes original Accursed train
666                                                  ⍝ ⍎ executes original Accursed train
</source>
</syntaxhighlight>


== External links ==
== External links ==

Navigation menu