these different functions are composed together like so
f1 -> f2 -> f3 -> f4 -> f5
I need to add two new functions fx and fy around f3 like so:
f1 -> f2 -> fx -> f3 -> fy -> f4 -> f5
so I need to pre-process the argument for f3 and then post-process the output from f3
here function fx makes a change and fy reverts the change back, but fy needs additional details from fx to be able to revert, to know what values to revert to for example
the problem is that function fx needs to produce two outputs
first: output that is needed by f3
second: output that is needed by fy
Question: How to share dependencies between functions that are not connected together direct, is there a spacial way/technique to solve this?
FYI, I'm using Java8
I will use some type A
for all function return values; you will need to sort out the real types yourself.
Here's the intention as you described it:
fx
will yield two parameters, let's call them a
and b
.f3
will process the value a
and yield a'
.fy
will consume a'
and b
and produce c
.Let's see how can we achieve that (I will write pseudocode for type signatures):
Define fy
as a function that takes two parameters and returns one:
(A, A) => A
Define fyp
(short for "fy preprocessed") as a function that takes some preprocessing function p
and performs the logic of your fy
, but with first parameter preprocessed with p
. We will write this in the form P => FY
, where P
is the type signature of the preprocessing function, and FY
is the type signature of fy
:
P => FY, which expands to
(A => A) => (A, A) => A
So the implementation of this function should take the preprocessing function p
of type A => A
as input, and perform the logic of fy
(this is the right hand side, (A, A) => A
... note how it corresponds to the signature of fy
), but before performing the logic, it will map the first parameter of fy
with p
. Here's some Scala implementation for reference (I'm not so good with Java):
val fyp: (A => A) => ((A, A)) => A =
f => fyuParams => performFyLogic((f(fyuParams._1), fyuParams._2))
where performFyLogic
is the actual processing logic of your fy
function.
Define the final composition as:
f1 -> f2 -> fx -> fyp(f3)
Here's full Scala implementation for reference (again, don't know Java 8 that well):
val f1: Int => Int = _ + 1
val f2: Int => Int = _ + 2
val f3: Int => Int = _ + 3
val fx: Int => (Int, Int) = i => (i + 4, i + 5)
// let's assume that the original fy simply sums up its two parameters
val fyp: (Int => Int) => ((Int, Int)) => Int =
f => fyArgs => f(fyArgs._1) + fyArgs._2
val composed1 = f1 andThen f2 andThen f3
println(composed1(42)) // 48
val composed2 = f1 andThen f2 andThen fx andThen fyp(f3)
println(composed2(42)) // 102
// because:
// f1 -> f2 -> fx = (42 + 1 + 2 + 4, 41 + 1 + 2 + 5) = (49, 50)
// fyp(f3)((49, 50)) = (49 + 3) + 50 = 102