Before I am asking for more let me say that the help I got from this forum is excellent and very friendly. Alex's weekly course videos and his other videos as well as the recorded frequently faq sessions (also with Lucy) are great. And last but not least I really appreciate the userbase and all its help documents; finally Hoogle can be of help even if this can be much to digest for a beginner. Many, many thanks for that!
In another posting I did mention that I also found Carsten Heisterkamp's introduction quite helpful. Especially the list of guidelines to explore and first and foremost to combine Tidal functions is something which for me - and I suppose any beginner - is potentially fruitful. Under the heading "Composing functions the right way" he writes:
Tidal really gets fun when you learn how to write your patterns and manipulate and alter them by intuitively sticking functions together without being interrupted by the compiler and searching for bugs in your code. This is most of the time the case, either because of typos and most of the time because we tried to combine functions with signatures which do not fit.
I am quite aware that the internals of type signatures and related language internals are not everyone's cup of tea but I really like to dive in and found Heisterkamp's argument quite convincing (for me). This is where I started a few months ago. Besides the mentioned article there are some resources about expressions like $
and #
and I guess I meanwhile have consumed all of it (which obviously does not mean: understood all of it). I think I now can interpret type signatures and I have an idea about how these can help to find out how to combine Tidal functions.
On the other hand I still have a lot of questions, am confused especially about syntax and find myself using trial-an-error-methods (interesting to explore a programming language solely with empirical methods like it was nature ), which is not wrong but I'd appreciate also a logically guided and systematic way.
So here are some very helpful yet also confusing hints from the mentioned article (see the chapter titled "Composing functions the right way"); this is step 4 and 5 he provides with reference to a reasonably complex example:
"4. If you want to use a function that returns a pattern inside a function that takes a pattern of type ControlMap, you need to evaluate it first with the bracket () notation, like juxcut (# speed 2)"
"5. Vice versa if you want to use a function which can not be used with the # because they don´t take but return pattern inside a function that takes a pattern you also need to evaluate them first with the bracket notation ().For example # pan (slow 4 $ sine ) or # gain(choose [0.5 0.75 1])
The example code is:
d1 $ slow 4
$ spaceOut (map (1/) [1, 1.2 .. 10])
$ sometimesBy 0.66 (rev)
$ rarely (juxcut(# speed (choose[0.33, 0.5, 0.66, 0.75, 1.5, 2 ])))
$ sound “bd*4”
# n (choose[1, 3, 5, 7])
# pan (slow 4 $ sine )
# orbit 0
Now looking at the type signatures tells me:
juxcut :: (Pattern ControlMap -> Pattern ControlMap) -> Pattern ControlMap -> Pattern ControlMap
and
speed :: Pattern Double -> ControlPattern
In this respect hint #4 makes some sense (leaving also some darkness):
As far as I have understood a ControlMap
is or can be a subunit of a control pattern and serves to directly control sound resp. Supercollider (pardon me if this is very simplistic but it is what I have grasped so far). In really simple words: As speed
supplies a pattern and juxcut
expects one, the #
function is necessary to combine both (which does not really seem to be the correct explanation now that I am reading my text again...).
There are other similar cases such as every
I e. g. found in an example from a Kindohm interview:
d1
$ every 3 (0.5 <~)
$ every 4 (chop 4)
$ every 5 (# speed "1.5 -1 0.5")
$ every 6 (# crush 2)
$ every 7 (rev)
$ stack [ sound "bd(3,8)", sound "cp*2"]
speed :: Pattern Double -> ControlPattern
crush :: Pattern Double -> ControlPattern
pan :: Pattern Double -> ControlPattern
The type signature of every
is:
every :: Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
So this is obviously a case where the Heisterkamp rule #4 applies but this rule does not seem to be defined as general as it should or could: every
does not expect a 'ControlPattern' but - patterns of type int and a and a function.
There are other functions where the #
is not necessary such as
$ every 7 (rev)
rev :: Pattern a -> Pattern a
and
$ every 2 (chop 4)
chop :: Pattern Int -> ControlPattern -> ControlPattern
So I am sure this is all explainable in a very logic way. It is just that I can't see it with the knowledge I have been able to gain so far.
Finally I am not really sure how the rule #5 fits into the image. Actually I see the reference within the code example but I can't really map the rule with the type signatures:
# pan (slow 4 $ sine )
slow :: Pattern Time -> Pattern a -> Pattern a
pan :: Pattern Double -> ControlPattern
It might be, that my lack of understanding rises from the fact that I don't really understand the conceptual differences between ControlMap
, ControlPattern
and some other Pattern
.
I really do hope that I was able not only sound like I am complaing about my confusion but also to somehow give it a form that someone more experienced can pick up the tread and help to clear this up. It is somehow quite difficult to come up with clear questions (usually once you have a clear question the answer is not far away).
I guess my main question is, whether it is possible to extend the already awesome resources that exist with a document titled something like "Syntax tips for stitching Tidal functions together" (sorry, English is not my first lanuage).
I reckon this would not only help me but also other beginners.
Anyway, if you have come to this point: Thanks for reading these lengthy notes!