-
-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pipeK for monads #380
Comments
Wouldn't we still need to put something into a Kleisli at the beginning and take it out at the end? |
I'm thinking about some like Kleisli Arrows with a compose :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c) I haven't thought out the particulars, but calling the result is going to return a monad. |
Maybe Kleisli :: Monad m => forall a b. m -> (a -> b) -> (a -> m b) ? |
Another option is to just make const k = Kleisli(a => [a]); // The Kleisli identity?
const l = Kleisli(a => [a, a]);
compose(l, k)(1); // [1, 1] |
Since S.pipe([S.chain(f), S.chain(g), S.chain(h)]) With S.pipeK([f, g, h]) With S.pipe([S.Kleisli(f), S.Kleisli(g), S.Kleisli(h)]) |
S.pipe(S.map(S.chain, [f, g, h])) const pipeK = S.compose(S.pipe, S.map(S.chain)) I'm happy defining it myself for my sporadic use-cases. 🤷♂️ |
@davidchambers, I was thinking the same thing yesterday. I think we should still add it though. Aldwin's implementation looks good. |
Aldwin's definition is very elegant, but it relies on partial application. Since we don't use function pipeK(fs, x) {
return Z.reduce(function(x, f) { return Z.chain(f, x); }, x, fs);
} |
Gabe, would you like to submit a pull request for this? |
Is this released? I'm getting:
Version
|
@hbarcelos it's merged into |
I hope to release v0.15.0 this weekend, by the way. :) |
@gabejohnson I saw this issue, saw the merged label and tried to use this function 😅. @davidchambers OK, I'll wait until then :) |
@gabejohnson @davidchambers Any interest in revisiting this and doing it in terms of |
That sounds good to me, @masaeedu, provided the performance is acceptable. |
How is this different from my proposal above? |
It's probably not, but it looks like the implementation ended up being a specialized pipeK for one reason or another. A pipeC function that accepts the typerep of a category would result in pipeC(Function) being pipe and pipeC(Kleisli) being pipeK. |
It would look something like this (but with the implementations fetched from the env by typerep): const Fn = (() => {
const id = x => x
const compose = f => g => a => f(g(a))
return { id, compose }
})()
const Kleisli = M => {
// :: a -> m a
const id = M.of
// :: (b -> m c) -> (a -> m b) -> (a -> m c)
const compose = bmc => amb => a => M.chain(bmc)(amb(a))
return { id, compose }
}
const pipeC = C => arr => arr.reduce((p, c) => C.compose(c)(p), C.id)
// Monad for demonstrating kleisli composition
const Arr = (() => {
const of = x => [x]
const chain = f => x => x.reduce((p, c) => [...p, ...f(c)], [])
const range = n =>
Array(n)
.fill(undefined)
.map((_, i) => i + 1)
return { of, chain, range }
})()
const pipe = pipeC(Fn)
const pipeK = M => pipeC(Kleisli(M))
const tests = [
pipe([x => x + 1, x => x * 2])(5),
pipeK(Arr)([Arr.range, x => (x % 2 ? [x, '👏'] : [x, '👏 👏'])])(4)
]
console.log(tests) There's many interesting things that form categories (among them lenses), and |
Ramda has Kleisli composition via composeK and pipeK. We probably only need one of those.
It is worth considering instead a specification for Categories as suggested in fantasyland/fantasy-land#208 and implementing a Kleisli type that exposes its own
compose
.This would mean
pipe
would be sufficient if it were implemented in terms ofcompose
.The text was updated successfully, but these errors were encountered: