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.
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?
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)
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:
If
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.
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 found myself reminded of inhabit while reading your latest example, and came up with the below which seems to do as expected though with less new code and more usage of existing Tidal functions:
(import Data.Functor (bimap))
replace x = inhabit $ bimap id pure <$> x
or this, if you don't want to have to put strings in the table:
replace x = inhabit $ bimap show pure <$> x
(Alternatively, one could copy the code of inhabit itself and (either or both):
Replace its String dependency with an Eq constraint
Fall back on id instead of silence
Or, if you're okay with writing String keys and writing pure with the values, you can just use inhabit directly and with a potential (though likely overkill) benefit of being able to pattern directly inside the table values themselves. A whole 'nother layer of meta.
)
together with this:
meta pf p = pf >>= ($ p)
I'm curious, though, as to whether or not these implementations cover the same requirements.
While doing some reading and working on some other things after posting this I realized that my take on meta is just barely more than bind. I'll update my post to reflect that.
Your insights, @mvdirty, render my definition of meta unnecessary -- I'd rather write pf >>= ($ p). But maybe replace still deserves to exist. I've updated the pull request accordingly.