Has anyone defined an `ap`-like function `:: f (f a -> f b) -> f a -> f b` where `f = Pattern`?

The Applicative class exists:

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

If this were a class too, it would look something like:

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

I defined such a function in Montevideo. I'll probably do the same for Tidal if it hasn't already been done, but it'd be cool if it had.

I just defined maybe the gnarliest regex of my life to look for anything resemblilng f (f a -> f b) -> f a -> f b in the source code, and no dice:

-- Haskell:
word = "[0-9a-zA-Z_]\\+ *"
lp = "( *"
rp = ") *"
arrow = "\\-> *"
w2 = word ++ word
f2 = word ++ word ++ arrow ++ word ++ word -- func from 2 words to 2 words
re = word ++ lp ++ f2 ++ rp
     ++ arrow ++ w2
     ++ arrow ++ w2

-- Emacs: In `re`, replace each \\ with \, because I apparanetly still don't understand strings in Haskell well enough.

-- Shell:
grep -i "[0-9a-zA-Z_]\+ *( *[0-9a-zA-Z_]\+ *[0-9a-zA-Z_]\+ *\-> *[0-9a-zA-Z_]\+ *[0-9a-zA-Z_]\+ *) *\-> *[0-9a-zA-Z_]\+ *[0-9a-zA-Z_]\+ *\-> *[0-9a-zA-Z_]\+ *[0-9a-zA-Z_]\+ *" -r . --color

There's actually one match, in "tidal-parse/src/Sound/Tidal/Parse.hs", but that's not relevant.

1 Like

Nice. I love me a typed programming challenge ...

  1. regexp .. well. For searching by type, I use f (f a -> f b) -> f a -> f b - Hoogle But, there is no such thing (in the APIs of packages on stackage)

  2. I was trying to use GitHub - augustss/djinn: Generate Haskell code from a type but it's not polymorphic enough (?).

  3. The type alone does not tell me what you expect the function to do. What laws do you expect to hold? Would this work for other Applicatives? I guess you need Monad as well?

  4. I built this, which has the type you want:

\ g x -> join (fmap (\h  -> h . pure )  g <*>  x )
  :: (Monad m, Applicative f) => m (f a1 -> m a2) -> m a1 -> m a2

if m = f = Pattern, then for join, there are three choices (inner, outer, squeeze), and for <*> there are as well (structure from left, right, both)

This sounds to me like a patternized version of spread with the arguments rearranged a bit.

Specifically, something like:

spread ($) [id, rev, fast 2, echo 0.5] pat

could instead be represented as

ap (listToPat [id, rev, fast 2, echo 0.5]) pat

I don't think there's a short mininotation for a pattern of functions, though, so I'm not sure how useful ap is here...

@jwaldmann What are inner, outer and squeeze?

I agree that it's not clearly a great candidate for a typeclass. I don't know what rules it should satisfy for other types.

For the Pattern type, the law it should satisfy is, I believe, this:


f :: Pattern ( Pattern a -> Pattern b )
a :: Pattern a
b :: Pattern b
s :: State

and query f s yields a single event e covering the entire timespan of s with the payload g :: a -> b, then query (f <**> a) s should be equal to query (g a) s.

patternized version of spread

@bgold Sweet! Yes, that's exactly what it is.

don't think there's a short mininotation for a pattern of functions, though

I thought about that and it seems hard. But it's easy to make a pattern of words (or numbers) and then use a lookup function to replace the words (or numbers) with functions. The awkward part is that since some functions take arguments, you'll need to apply the arguments in the lookup table rather than in the pattern -- but it's nice and simple:

funcPat :: forall a b label. Show label
        => [(label, Pattern a -> Pattern b)]
        -> Pattern String
        -> Pattern (Pattern a -> Pattern b)
funcPat funcs labelPat = let
  m :: Map String (Pattern a -> Pattern b)
  m = M.fromList $ map (_1 %~ show) funcs
  in filterJust $ fmap (flip M.lookup m) labelPat

(That function could be more general, too -- the Pattern a -> Pattern b could be swapped out for a type variable.)

I meant innerJoin, etc. ( Tidal/Pattern.hs at main · tidalcycles/Tidal · GitHub )