Functor, Applicative and Monad Pattern

I was reading this interesting post for the hundredth time and I am still trying to understand what's behind the different instances of Pattern and especially when it is useful to handle Monad Pattern, Functor Pattern and Applicative Pattern.
Here is what I got so far:

  • Functor Pattern comes handy when we want to use function from e.g. the standard Prelude module but we don't know how to cope with the Pattern context. We use <$> and we get a result wrapped in a Pattern context that we can use inside Tidal.
    E.g. chop (round <$> (slow 8 $ range 1 128 $ sine)) - thanks @nuelmyr for the pattern
  • Monad Pattern comes handy when we have a function that returns a Pattern context, but it does not accept a value in a context. We use >>= and the Monad Pattern will apply the function to the Pattern and we get a result wrapped in a Pattern context.
    I think the example from the post is quite explicative:
    fastFromList[False,False,True] >>= \ f -> if not f then run 2 else run 3

But what about Applicative Pattern: what is a pattern of functions (Pattern (a -> b))?


I am very interested in seeing some examples on how you use the different instances of Pattern to create your own functions.

Two more points:

  • the last example in the post makes use of >>>= which I cannot locate in the docs: what does that mean? I tried to evaluate it but it does not work.
  • what is a Pattern (Pattern a)?
    I struggle to understand what a pattern of patterns is, and when it is used.

Using Hoogle, I could find this that references >>>==, but I can't make anything of it.

Functor is indeed for running a function over all of the values inside a pattern. <$> is an infix alias for fmap, which is conceptually the same as map, but works for all functors, not just lists.

round <$> ("1 2.2 3" :: Pattern Double)

Functor is useful for functions with a single input like round, but how about min, which takes two inputs and returns the smallest one?

Well just giving it one input is legal haskell:

min <$> ("1 2 3" ::Pattern Double)

(I have to put the type signature in there, otherwise it can't tell what kind of number it is, because there's no context for it to infer it from..)
The result is a pattern of functions - Pattern (Double -> Double)

That's not too useful though.. How to turn it into a pattern of doubles? Well the Applicative instance give you this:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Pattern is an Applicative instance, so we can fill that in:

(<*>) :: Applicative f => Pattern (a -> b) -> Pattern a -> Pattern b

So that's what it's for, applying a pattern of values to a pattern of functions, giving you a pattern of results..

min <$> ("1 2 3" ::Pattern Double) <*> ("5 0 10")

That's the same as "1 0 3". Useful!
(Tidal also defines <* and *>, which does the same but takes the structure from the left or right, rather than both.. that's where the |+ vs +| vs |+| behaviour comes from. This overloads the definition of <* and *> in the prelude, beware..)

A pattern of patterns is a pattern that has other patterns as values. They crop up a lot internally. For example fast didn't used to take a pattern as it's first argument. Fixing this was a case of doing something like this: newfast timepat pat = innerJoin $ (\time -> fast time pat) <$> timepat

That is, mapping fast over the values of timepat, ending up with a pattern of patterns.. Then using a join to turn a pattern of patterns into a pattern. (innerJoin means that the structure comes from the patterns inside the patterns..)

(>>>=) isn't a standard operator, it's defined in the post.

6 Likes

Aaah so you can do partial application, and this is what gives you: pattern of functions!
It so does make sense!

There is not need to specify the context for "5 0 10" because it is already set by Pattern (Double -> Double)?

And thanks Alex, this is so amazing and it will keep me busy for a while :slight_smile:

@yaxu I have two follow up questions.

I checked the type for two similar expressions

(\x -> segment 3 $ irand x) <$> "3 6 9"
  :: (Functor f, Num a, Data.String.IsString (f Int)) =>
     f (Pattern a)
(\x -> segment 3 $ irand x) <$> (irand 6)
  :: Num a => Pattern (Pattern a)

I understand the latter but not the former: I see that both are something made of (Pattern a)
but I don't understand the f. Does that mean a Functor Pattern? And why the difference in type?

Moreover, I struggle to understand how differently innerJoin and outerJoin operate.
Perhaps you have an example to show?

Hi.

Both expressions can be used at type Pattern (Pattern Int).

GHC infers the most general type. All the ingredients are polymorphic, e.g., irand :: Num a => Int -> Pattern a creates the Num a constraint.

Because of OverloadedStrings, we have "3 6 9" :: IsString a => a, so it's not even assumed to be Pattern a. But since we apply <$>, the Functor constraint appears. Etc.

Here is a minimal example for the joins:

Prelude Sound.Tidal.Context> outerJoin $ pure $ fastcat [pure False, pure True]
(0>1)|False

Prelude Sound.Tidal.Context> innerJoin $ pure $ fastcat [pure False, pure True]
(0>½)|False
(½>1)|True

outer pattern is of period one (because pure), and at each beat, it produces the same inner pattern, whose structure we see, or don't.

NB: @yaxu I wanted to outerJoin $ slowcat [ run 2, run 3 ] but this gives

*** Exception: enumFromTo: not supported for patterns

[EDIT] ghc is right and I was wrong, since I confuse it with slowcat [ run 2, run 3] :: Pattern Int. I actually meant

innerJoin ( slowcat [ pure $ run 2, pure $ run 3] :: Pattern (Pattern Int))

Sorry for the confusion!

NB: It's nice exercise to figure out why the expression outerJoin $ slowcat [ run 2, run 3 ] wasn't rejected by the type checker.

Answer: the numeric literals 2,3 are polymorphic! Let's ask ghc for the type of 3:

outerJoin $ slowcat [ run 2, run (3 :: _)]

<interactive>:12:40: error:
    • Found type wildcard ‘_’ standing for ‘Pattern (Pattern a)’

Also,

run :: (Enum a, Num a) => Pattern a -> Pattern a

so here we'd need an instance Enum (Pattern a) which does exist but it only implements succ, pred not enumFromTo, which is called by run.

1 Like

Thanks for your answer, it is clearer now!
One thing: what is pure?

in general, https://hoogle.haskell.org/?hoogle=pure&scope=set%3Astackage

pure :: Applicative f => a -> f a

in Tidal, pure x is a pattern that always has value x (with period 1). Often, usage of pure is invisible, but these are indeed the same:

Prelude Sound.Tidal.Context> "0" :: Pattern Int
(0>1)|0
Prelude Sound.Tidal.Context> pure 0 :: Pattern Int
(0>1)|0
1 Like

Sorry for the non-linear edit above. To conclude (for now ...), here's another pair of examples for inner/outerJoin:

outerJoin ( fastcat [ pure $ run 2, pure $ run 3] :: Pattern (Pattern Int))
(0>½)|0
(½>1)|1

innerJoin ( fastcat [ pure $ run 2, pure $ run 3] :: Pattern (Pattern Int))
  (0>½)|0
⅓-(½>⅔)|1
  (⅔>1)|2