A join function has type Pattern (Pattern a) -> Pattern a.
There are several joins (innerJoin, outerJoin, unwrap, squeezeJoin) in the tidalcycles library, and they are used to implement other functions. Looking at tidalcycles performances, it is my impression that joins rarely occur visibly in applications.
Here are two examples that show why joins might be useful:
https://www.imn.htwk-leipzig.de/~waldmann/etc/untutorial/tc/join/
If you have more examples, or questions, regarding joins, bring them to the workshop https://www.imn.htwk-leipzig.de/LFUNK2026/ (September 29), or/and post them here beforehand.
1 Like
I am a huge fan of squeezeJoin. Naturally it is used with the inhabit function. I use this to create progressions and drum beats. It means, that you can control a pattern with a pattern like this:
do
let drum pt f = inhabit [
("1", f $ s "[bd(3,8), hh*4]" )
,("2", f $ s "[bd*4, ~ sn, hh*4 hh*8]" )
,("f1",f $ s "sn!2 bd!4 sn!2 tom:1! tom:0!" )
] pt
bubu = (fast "<1 1 2 1>" . ply "<1!3 1>" )
d1 $ drum "<1 2 1 f1>" (bubu)
And then you can decide whether you want to apply a function on the outer or the inner pattern. I have a custom implementation for this but basically I use it like in the code snippet above, i.e.
drums (inner_pattern_function "1") outer_pattern_function
For a live coding performance I use numbers to control a pattern, because it is really easy to change the numbers with a simple shortcut in NeoVim.
Ah, I didn't know about inhabit. I just looked it up https://hackage-content.haskell.org/package/tidal-core-1.10.2/docs/src/Sound.Tidal.UI.html#inhabit and it's basically doing what I did in my first example (replace string with pattern)? Ok, then we (and the library authors) agree that it's a useful application of join.
(end of statement, begin of opinion)
Where we might disagree is whether inhabit is above the https://wiki.haskell.org/Fairbairn_threshold. Their example is: you wouldn't need a function fooBar if the implementation is just foo . bar. Well, if you know what the dot means (function composition). The point is, "join" is very much similar (basic and thus, important) as it allows for composing functions a -> Pattern b (cf. https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Monad.html#v:-62--61--62- )