1

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.

user1713450
  • 1,307
  • 7
  • 18
  • 1
    I'm not very familiar with fp-ts but here is the first idea I have for this - you can turn `foo` and `bar` into thunks. A thunk is a function that takes no parameters - often it's made *from* a function that takes parameters, so you can just do `partial(foo, a)` to get one. You now have two functions that when executed produce a result. Maybe there that makes it easier to handle them as you don't need to manage the inputs. – VLAZ Jul 03 '20 at 05:04
  • 2
    Why are you so keen to write this point-free? (In Haskell it's called [`bimap`](http://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Bifunctor.html#v:bimap), by the way.) – Benjamin Hodgson Jul 03 '20 at 05:25
  • Just an excuse to practice PF since I'm new to it. I've been writing an API library where I have a `fn1 = (a:A) => B`, and it gets composed with other functions, and ultimately `export const apiCall = (a:A) => pipe(fn1(a), ...)` and I like being able to reduce the number of times I explicitly provide the params. With fp-ts I can do `export const apiCall = flow(fn1, ...)` and never provide the params. So I avoid duplication and it makes refactoring easier. That's what made me interested in PF and now I'm going hard at it. – user1713450 Jul 03 '20 at 05:29
  • Just in this case, it's not as straightforward. I have two functions that rely on *parts* of some initial parameters and I'd like to avoid `export const apiCall = (a:A, b:B) => combine(fnTakingA, fnTakingB)` if possible – user1713450 Jul 03 '20 at 05:30
  • For now I've settled on treating these as `Reader<{A,B}, C>` and I can use `Reader.local` to mutate the environment `{A,B}` passed to `fnA` and `fnB` and that has solved my problem. Just feels inelegant. – user1713450 Jul 03 '20 at 05:32
  • Do you care that `newFn` will be point-free, or do you want to implement the `magicCompose` itself in a point-free way? – Bergi Jul 03 '20 at 19:08
  • 1
    @BenjaminHodgson Also the arrow generalisation [`***`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Arrow.html#v:-42--42--42-) :-) – Bergi Jul 03 '20 at 19:10

0 Answers0