(This joke has already been claimed by APL, unfortunately)
Also see this tutorial section for an introduction that doesn't require so much context to understand.
The "hook" combinators Before and After serve a few purposes in BQN. The important thing to remember: the pointy side goes towards the first function to be executed, and the next function that returns the final result is at the ring side. If the pointy-side function is actually a constant like a number, then the ring-side function just gets applied to that constant and one of the arguments. This is the thing Haskell programmers are constantly telling each other isn't called currying, or "Bind" in BQN.
Name | Cmp |
Cmp π© |
π¨ Cmp π© |
Unified | Train |
---|---|---|---|---|---|
Before | FβΈG |
(Fπ©) G π© |
(Fπ¨) G π© |
{(π½π¨β£π©)πΎπ©} |
Fββ£ G β’ |
After | FβG |
π© F (Gπ©) |
π¨ F (Gπ©) |
{(π¨β£π©)π½πΎπ©} |
β£ F Gββ’ |
In the general case, I think of Before as using π½
as a preprocessing function applied to π¨
(when there are two arguments), and After as using πΎ
as preprocessing for π©
. Then the other operand is called on the result and remaining argument. Here are some simple calls with Pair (β
): the result is a pair that corresponds to π¨βΏπ©
, but one or the other result has been modified by the pointy-side function.
9 ββΈβ 2 β¨ 3 2 β© 9 βββ 2 β¨ 9 β¨ 0 1 β© β©
When only one argument is given, it's used in both positions, so that the arguments to the final function are π©
and a function applied to π©
.
βββ 5 β¨ 5 β¨ 0 1 2 3 4 β© β©
This can be used to make a "filter" pattern using Replicate (/
). The difference is that Replicate takes a list π©
and boolean list π¨
indicating which elements to keep, but filter should take a list and a function that says whether to keep each element. The pattern is FΒ¨βΈ/ x
, expanding to (F¨x) / x
. Here's a list filtered with the function {π©<0}
.
{π©<0}Β¨βΈ/ 4βΏΒ―2βΏ1βΏΒ―3βΏΒ―3 β¨ Β―2 Β―3 Β―3 β©
As <
is a pervasive function, there's no need for the Each (Β¨
) in this case, and the clunky block function {π©<0}
can also be written smaller with a combinator, as <β0
. More on that in the next sectionβ¦
<β0βΈ/ 4βΏΒ―2βΏ1βΏΒ―3βΏΒ―3 β¨ Β―2 Β―3 Β―3 β©
"Bind" isn't a special case of Before and After, but instead a description of one way to use them. Let's take a look at the example from the previous section:
βοΈ<β0 4βΏΒ―2βΏ1βΏΒ―3βΏΒ―3 β¨ 0 1 0 1 1 β©
If we expand <β0 x
, we get x < (0 x)
, which doesn't quite make sense. That's because 0
has a subject role, but β
always applies its operands as functions. It's more accurate to use x < (0{π½} x)
, or just skip ahead to x < 0
.
Similar reasoning gives the following expansions:
Cmp |
0βΈ< |
<β0 |
---|---|---|
Cmp x |
0 < x |
x < 0 |
w Cmp x |
0 < x |
w < 0 |
Note that when there are two arguments, the constant "swallows" the one on the same side, so that the function is applied to the constant and the argument on the opposite side.
As in a train, if you want to use a function as a constant then you need to be explicit about it, with the Constant (Λ
) modifier.
3 ββ(βΛ)βΈβ₯ 'a'+β12 ββ β΅"abcd efgh ijkl" β
In the more extreme case of wanting a modifier operand, you might try ββ({β}Λ)βΈβ₯
, or (β£β{β}Λ)βΈβ₯
, or just cheat with βΎββ¨ββ©βΈβ₯
.
If you like to go tacit, you'll likely end up stringing together a few βΈ
s and β
s at times. Of course the effects are entirely determined by the left-to-right precedence rule for modifiers, but it's interesting to examine what happens in more detail.
In the pattern FβΈGβH
, the ordering doesn't matter at all! That is, it means (FβΈG)βH
, but this is the same function as FβΈ(GβH)
. In both cases, F
is applied to π¨
, H
is applied to π©
, and G
acts on both the results (the parentheses do change whether F
or H
is called first, which only matters if they have side effects).
4 -βΈβββ 2 β¨ Β―4 7.38905609893065 β©
I once named this pattern "split compose", but now I think it makes more sense to think of it as two pre-functions added separately to one central function (β
above). The whole is exactly the sum of its parts. When applied to just one argument, π©
is reused on both sides, making the composition equivalent to a 3-train.
-βΈβββ 2 β¨ Β―2 7.38905609893065 β© (-ββ) 2 # Same thing β¨ Β―2 7.38905609893065 β©
More β
s can be added on the right, making π©
flow through all the added functions. So for example FβGβH x
is x F G H x
, and could also be written Fβ(G H) x
.
A sequence of βΈ
s is more interesting. It doesn't just compose the functions (for that you need GβFβΈH
, but note the weird orderingβF
applies before G
!), but instead passes the current value and the initial function each time. Consider FβΈGβΈHβΈI
, or ((FβΈG)βΈH)βΈI
: every function but F
is on the ring side, meaning it's dyadic!
Here's a long example, that might show up if you want to sort an array but have an intolerance for the character β§
. In quicksort, you select a partition element from the array, then divide it into elements less than, and greater than or equal to, the pivot. You'd probably pick a random element for the pivot, but here I'll go with the middle element to avoid having a webpage that generates differently every time!
(ββ Γ·2Λ) "quicksort" # Index of the pivot 4 (ββ Γ·2Λ)βΈβ "quicksort" # Select pivot from π© 'k' (ββ Γ·2Λ)βΈββΈβ€ "quicksort" # Compare with π© β¨ 1 1 0 0 1 1 1 1 1 β© (ββ Γ·2Λ)βΈββΈβ€βΈβ "quicksort" # Use to partition π© β¨ "ic" "quksort" β©
Three is rare, but I use two βΈ
s all the time, as well as β
followed by βΈ
, for example the <β'a'βΈ/
filter on the front page. I think a combination like lotsβofβstuffβΈ/ x
reads very nicely when moving from right to left. When I see βΈ/
I know that I'm filtering x
and can read the rest with that context. The reason βΈ
has all this power, but not β
, has nothing to do with the modifiers themselves, as they're completely symmetrical. It's all in the way BQN defines modifier grammar, left to right.