TIL: `join` over `(->)`
I discovered a neat pattern: using join with the function arrow ((->)) as a Monad. When you join over functions, you get:
join :: (a -> a -> b) -> a -> b
join fn value = fn value valueThis lets you pass the same argument twice: once to determine what to do, and another to actually do it.
How I used it
In @wasp.sh, we have validators with the signature type Validator = input -> Validation. Most of our validation helpers are written in a point-free, un-applied style, which works great, except recently when I needed to inspect the input to decide which validator to run.
dependencyValidator :: V.Validator PackageJson
dependencyValidator depName = join $ \pkgJson ->
V.inField "dependencies" $
V.inField depName $
depVersionValidator (requestsLooseChecking pkgJson)
depVersionValidator :: Bool -> V.Validator DependencyVersion
depVersionValidator True = checkExistence `V.and` checkCorrectVersion
depVersionValidator False = checkExistenceThe join gives us access to pkgJson to inspect it (via requestsLooseChecking), while still threading it through to the composed validators.