I'm doing this in TypeScript, but would be OK with a response in another language with FP abilities.
Say I've got const foo = (a:A) => M
and const bar = (b:B) => N
. I'd like to run these functions "in parallel" (doesn't have to be actual parallel IO but they aren't dependent on each other) but by defining the "composition" in a point-free manner.
If they were actually composable (say a
returns B
instead of M
), I could write
const composed = flow(foo, bar) // (a:A) => N
Instead, I'd like some way to combine them them (let's call this magicCompose
) such that my point-free style yields
const newFn = magicCompose(foo, bar) // (a:A, b:B) => [M,N] or ({a:A, b:B}) => [M,N] or (p:[A,B]) => [M,N]
Is this a possibility?
Using fp-ts
in TypeScript I have sequenceT
that lets me treat these as Readers and combine them like so
const _ = sequenceT(reader)(foo, bar)
However, this is invalid if foo
and bar
take different params. I can use local: (E => R) => (Reader<E, any>) => Reader<R, any>
but then I have to break point-free to write
const _ = sequenceT(reader)(
local((params:{a:A,b:B}) => params.a)(foo),
local((params:{a:A,b:B}) => params.b)(bar)) // Reader<{a:A,b:B}, [M,N]>
which is effectively ({a:A,b:B}) => [M,N]
but unfortunately it's not point-free. If I were to make optics like _a = Lens.fromProp<{a:A,b:B}>()('a')
and _b = Lens.fromProp<{a:A,b:B}>()('b')
and then have
const _ = sequenceT(reader)(
local(_a.get)(foo),
local(_b.get)(bar)) // Reader<{a:A,b:B}, [M,N]>
But that seems like cheating to get point-free.