3

Suppose I have the following two functions.

def f1(f: T1 => Unit): Unit = ???
def f2(f: T2 => Unit): Unit = ???

I want to combine f1 and f2 to some functions f3

f3(f: (T1,T2) => Unit): Unit

I can implement it easy to such simple case

def f3(f: (T1,T2) => Unit) = f1 { t1 => f2 { t2 => f(t1,t2) } }

But is it possible to do in more generic way? If I have more than two functions with different arguments, how to combine them to make it possible to call with f: (T1,T2,...,TN) => Unit callback?

1
  • What are you trying to achieve here? Can you show an actual use case? Commented May 6, 2016 at 18:18

2 Answers 2

2

This is probably going to be overkill, but you could do something like this using shapeless.

We can represent a couple of side effecting callback functions as an HList :

import shapeless._

type Callback[A] = (A => Unit) => Unit

def f1(f: Int => Unit): Unit = f{ println("f1"); 5 }
val f2: Callback[String] = f => f{ println("f2"); "foo" }

val callbacks = f1 _ :: f2 :: HNil
// Callback[Int] :: Callback[String] :: HNil

Now we want to turn this last HList into a function which takes an (Int, String) => Unit as argument.

First we will introduce a type class OneCallback which can turn an HList like callbacks into one callback taking an HList as an argument (consisting all the different arguments of the multiple functions). For callbacks we will get a Callback[Int :: String :: HNil].

trait OneCallback[F, Args] {
  def apply(funs: F): Callback[Args]
}

object OneCallback {
  implicit val hnilOneCallback: OneCallback[HNil, HNil] = 
    new OneCallback[HNil, HNil] {
      def apply(funs: HNil): Callback[HNil] = callback => callback(HNil)
    }

  implicit def hconsCallback[A, FT <: HList, AT <: HList](implicit 
    oneCallbackTail: OneCallback[FT, AT]
  ) = new OneCallback[Callback[A] :: FT, A :: AT] {
    def apply(funs: Callback[A] :: FT): Callback[A :: AT] = 
      (f: (A :: AT) => Unit) =>
        funs.head { argH =>
          oneCallbackTail(funs.tail) { argsT =>
            f(argH :: argsT)
          }
        }
  }
}

Lastely we will create a function which can turn this Callback[Int :: String :: HNil] into a ((Int, String) => Unit) => Unit :

import shapeless.ops.function.FnToProduct

def oneCallback[CS <: HList, A <: HList, C](callbacks: CS)(implicit 
  ocb: OneCallback[CS, A],
  ftp: FnToProduct.Aux[C, A => Unit]
): C => Unit = c => ocb(callbacks).apply(ftp(c))

Now we can combine multiple callback functions as :

val cb = oneCallback(f1 _ :: f2 :: HNil)
cb { (i, s) => println(s * i) }
// f1
// f2
// foofoofoofoofoo
Sign up to request clarification or add additional context in comments.

Comments

2

I know this is not particularly elegant or easy to write but I think it's easy to implement and understand

val f1: String => Unit = s => println(s)
val f2: Int => Unit = i => println(i)
val f3: Double => Unit = d => println(d)


implicit class Chain[A](f: A => Unit) {
  def o [B](g: B => Unit): Tuple2[A, B] => Unit =  {
    case (x, y) => f(x); g(y)
  }
}

(f1 o f2 o f3) ("a" -> 1 -> 2.0)

EDIT: This is actually better than I expected.There's not a limit of 22 parameters as everything is a nested tuple2.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.