Train: Difference between revisions

From APL Wiki
Jump to navigation Jump to search
(History)
m (Text replacement - "</source>" to "</syntaxhighlight>")
(3 intermediate revisions by 2 users not shown)
Line 3: Line 3:
== Definition ==
== Definition ==


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:
Below, <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and <syntaxhighlight lang=apl inline>⍵</syntaxhighlight> refer to the arguments of the train. <syntaxhighlight lang=apl inline>f</syntaxhighlight>, <syntaxhighlight lang=apl inline>g</syntaxhighlight>, and <syntaxhighlight lang=apl inline>h</syntaxhighlight> are functions (which themselves can be tacit or not), and <syntaxhighlight lang=apl inline>A</syntaxhighlight> is an array. The arguments are processed by the following rules:


=== 3-trains ===
=== 3-trains ===
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:
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:
{|
{|
|<source lang=apl>  (f g h) ⍵</source>|| {{←→}} ||<source lang=apl>(  f ⍵) g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (f g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>(  f ⍵) g (  h ⍵)</syntaxhighlight>
|-
|-
|<source lang=apl>⍺ (f g h) ⍵</source>|| {{←→}} ||<source 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:
{|
{|
|<source lang=apl>  (A g h) ⍵</source>|| {{←→}} ||<source lang=apl>A g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (A g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>A g (  h ⍵)</syntaxhighlight>
|-
|-
|<source lang=apl>⍺ (A g h) ⍵</source>|| {{←→}} ||<source lang=apl>A g (⍺ h ⍵)</source>
|<syntaxhighlight lang=apl>⍺ (A g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>A g (⍺ h ⍵)</syntaxhighlight>
|}
|}


Line 22: Line 22:
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:
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:
{|
{|
|<source lang=apl>  (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g (  h ⍵)</source>
|<syntaxhighlight lang=apl>  (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (  h ⍵)</syntaxhighlight>
|-
|-
|<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>g (⍺ h ⍵)</source>
|<syntaxhighlight lang=apl>⍺ (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>g (⍺ h ⍵)</syntaxhighlight>
|}
|}


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]].
Only [[dzaima/APL]] allows <syntaxhighlight lang=apl inline>(A h)</syntaxhighlight>, which it treats as <syntaxhighlight lang=apl inline>A∘h</syntaxhighlight>.<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]].


[[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:
[[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:
{|
{|
|<source lang=apl>  (g h) ⍵</source>|| {{←→}} ||<source lang=apl>⍵ g (h ⍵)</source>
|<syntaxhighlight lang=apl>  (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>⍵ g (h ⍵)</syntaxhighlight>
|-
|-
|<source lang=apl>⍺ (g h) ⍵</source>|| {{←→}} ||<source lang=apl>⍺ g (h ⍵)</source>
|<syntaxhighlight lang=apl>⍺ (g h) ⍵</syntaxhighlight>|| {{←→}} ||<syntaxhighlight lang=apl>⍺ g (h ⍵)</syntaxhighlight>
|}
|}


Line 39: Line 39:
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.
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.


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>.
For example, the attempted [[#3-trains|fork]] <syntaxhighlight lang=apl inline>f/h</syntaxhighlight> is actually parsed as the [[#2-trains|atop]] <syntaxhighlight lang=apl inline>(f/)h</syntaxhighlight> and the attempted atop <syntaxhighlight lang=apl inline>f/</syntaxhighlight> 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 <syntaxhighlight lang=apl inline>f⊢⍤/h</syntaxhighlight> and the atop can be enforced by using the explicit Atop operator instead of a 2-train; <syntaxhighlight lang=apl inline>f⍤/</syntaxhighlight>.


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.
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, <syntaxhighlight lang=apl inline>1 0 1/⌽</syntaxhighlight> and <syntaxhighlight lang=apl inline>/,⊃</syntaxhighlight> are parsed as forks.


== History ==
== History ==
Function trains were first presented under the name "Phrasal forms" by [[Ken Iverson]] and [[Eugene McDonnell]] in a 1989 paper of the same name. They called the 2-function form a "hook" and the 3-function form a "fork" based on the shapes of the function call diagrams. On the return flight from [[APL88]], Iverson found the idea when he woke up from a nap and then developed it together with McDonnell. The use of syntax for trains followed a long history of attempts to define train-like behavior in terms of operators.<ref>[[Roger Hui]] and [[Morten Kromberg]]. [https://dl.acm.org/doi/abs/10.1145/3386319 ''APL since 1978'']. §3.8 Trains Encore. ACM [[HOPL]] IV. 2020-06.</ref>
Function trains were first presented under the name "Phrasal forms" by [[Ken Iverson]] and [[Eugene McDonnell]] in a 1989 paper<ref>[[Ken Iverson]] and [[Eugene McDonnell]]. [http://www.jsoftware.com/papers/fork.htm Phrasal forms] at [[APL89]].</ref> of the same name. They called the 2-function form a "hook" and the 3-function form a "fork" based on the shapes of the function call diagrams. On the return flight from [[APL88]], Iverson found the idea when he woke up from a nap and then developed it together with McDonnell.<ref>[[Roger Hui|Hui, Roger]]. [http://keiapl.org/rhui/remember.htm "Remembering Ken Iverson"]. 2004-11.</ref> The use of syntax for trains followed a long history of attempts to define train-like behavior in terms of operators.<ref>[[Roger Hui]] and [[Morten Kromberg]]. [https://dl.acm.org/doi/abs/10.1145/3386319 ''APL since 1978'']. §3.8 Trains Encore. ACM [[HOPL]] IV. 2020-06.</ref>


Trains as defined in Phrasal Forms were included in the first versions of [[J]] in 1990. They were added to [[NARS2000]] by 2009,<ref>NARS2000 Wiki. [http://wiki.nars2000.org/index.php?title=Trains&oldid=438 Trains]. Old revision: 2009-02-18.</ref> and [[ngn/apl]] had partial support in 2013. [[K]] defined a different and simpler kind of function train based on linear evaluation.
Trains as defined in Phrasal Forms were included in the first versions of [[J]] in 1990. They were added to [[NARS2000]] by 2009,<ref>NARS2000 Wiki. [http://wiki.nars2000.org/index.php?title=Trains&oldid=438 Trains]. Old revision: 2009-02-18.</ref> and [[ngn/apl]] had partial support in 2013. [[K]] defined a different and simpler kind of function train based on linear evaluation.

Revision as of 10:58, 11 September 2022

A function train is a compound function made up of a series of functions. It's written as an isolated expression (surrounded by parentheses or named) ending in a function. Defined by Ken Iverson and Eugene McDonnell in 1988 and added to Dyalog APL in 2014, trains are considered important for tacit programming and a characteristic of modern APL.

Definition

Below, and refer to the arguments of the train. f, g, and h are functions (which themselves can be tacit or not), and A is an array. The arguments are processed by the following rules:

3-trains

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:

  (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 ⍵)

2-trains

Most dialects define a 2-train is an atop, equivalent to the function derived using the Atop operator. The left function is applied monadically on the result of the right function:

  (g h) ⍵
g (  h ⍵)
⍺ (g h) ⍵
g (⍺ h ⍵)

Only dzaima/APL allows (A h), which it treats as A∘h.[1] See Bind.

J instead defines the 2-train as a hook, equivalent to the function derived using the Withe operator. The left function is always applied 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:

  (g h) ⍵
⍵ g (h ⍵)
⍺ (g h) ⍵
⍺ g (h ⍵)

Problems caused by function-operator overloading

Trains that use a 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.

For example, the attempted fork f/h is actually parsed as the atop (f/)h and the attempted atop f/ is actually parsed as a Windowed Reduction. There are multiple ways to mitigate this issue. For example, the fork can be enforced using the Atop operator by applying identity to the hybrid's result as f⊢⍤/h and the atop can be enforced by using the explicit Atop operator instead of a 2-train; f⍤/.

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, 1 0 1/⌽ and /,⊃ are parsed as forks.

History

Function trains were first presented under the name "Phrasal forms" by Ken Iverson and Eugene McDonnell in a 1989 paper[2] of the same name. They called the 2-function form a "hook" and the 3-function form a "fork" based on the shapes of the function call diagrams. On the return flight from APL88, Iverson found the idea when he woke up from a nap and then developed it together with McDonnell.[3] The use of syntax for trains followed a long history of attempts to define train-like behavior in terms of operators.[4]

Trains as defined in Phrasal Forms were included in the first versions of J in 1990. They were added to NARS2000 by 2009,[5] and ngn/apl had partial support in 2013. K defined a different and simpler kind of function train based on linear evaluation.

The train definition used in most APL dialects changes the 2-train from a hook to an Atop in behavior. This change was made in Dyalog APL 14.0 in 2014, under the direction of Roger Hui, who had argued for it by 2006.[6] It now appears in APL\iv, dzaima/APL, April, and BQN, and also matches the function composition featured in I in 2012.

External links

Documentation

Tutorials

Text

Videos

References

  1. dzaima/APL: Differences from Dyalog APL. Retrieved 09 Jan 2020.
  2. Ken Iverson and Eugene McDonnell. Phrasal forms at APL89.
  3. Hui, Roger. "Remembering Ken Iverson". 2004-11.
  4. Roger Hui and Morten Kromberg. APL since 1978. §3.8 Trains Encore. ACM HOPL IV. 2020-06.
  5. NARS2000 Wiki. Trains. Old revision: 2009-02-18.
  6. Roger Hui. Hook Conjunction? J Wiki. First published 2006-05-24.
APL syntax [edit]
General Comparison with traditional mathematicsPrecedenceTacit programming (Train, Hook, Split composition)
Array Numeric literalStringStrand notationObject literalArray notation (design considerations)
Function ArgumentFunction valenceDerived functionDerived operatorNiladic functionMonadic functionDyadic functionAmbivalent functionDefined function (traditional)DfnFunction train
Operator OperandOperator valenceTradopDopDerived operator
Assignment MultipleIndexedSelectiveModified
Other Function axisBracket indexingBranchStatement separatorQuad nameSystem commandUser commandKeywordDot notationFunction-operator overloadingControl structureComment