I am trying to create a collection of Unit
functions that have any arity and type, and will execute them in order.
This is the working implementation that I have:
/** A Subroutine simply represents a unit function that takes any number of parameters. */
type Subroutine = (Any) => Unit
/** A Routine represents a collection of functions which may consume a collection of parameters and will always
* complete an action.
*/
type Routine = List[(Subroutine, Seq[_])]
/** This function will consume a list of functions executing them in order.
*
* @param routine is the list of functions and their parameters to be consumed.
*/
def executeRoutine(routine: Routine): Unit = {
routine.headOption match {
case Some((f, x)) =>
f(x)
executeRoutine(routine.tail)
case None => ()
}
}
and when I declare a function or "subroutine", I have to declare it like this in order to extract the parameters:
def myFunction: Subroutine = {
case (firstParam: Int) :: (secondParam: String) :: _ => ...
case _ => throw new InvalidParameterException()
}
def myFunction2: Subroutine = {
case (firstParam: Int) :: _ => ...
case _ => throw new InvalidParameterException()
}
val routine: Routine = List(
(myFunction, Seq(firstParam, secondParam)),
(myFunction2, Seq(firstParam))
)
I realize that this is not close to idiomatic Scala code, and I've been working on implementing a different approach which will partially apply functions one parameter at a time, and then execute the function after the parameters are fully applied. I do not yet have a working version of this approach, but this is as far as I have gotten:
/** A Subroutine simply represents a unit function that takes any number of parameters. */
sealed abstract class Subroutine extends (Any => Unit)
case class Curried(partiallyApplied: Function[_, Function[_, _]], unapplied: Function[_, _]) extends Subroutine
case class Uncurried(function: Function[_, Unit]) extends Subroutine
/** A Routine represents a collection of functions which may consume a collection of parameters and will always
* complete an action.
* Using HList for the parameters, from Shapeless
*/
type Routine = List[(Subroutine, HList)]
/** This function will consume a list of functions executing them in order.
*
* @param routine is the list of functions and their parameters to be consumed.
*/
def executeRoutine(routine: Routine): Unit = {
routine.headOption match {
case Some((f, x)) =>
executeSubroutine(f, x)
executeRoutine(routine.tail)
case None => ()
}
}
private def executeSubroutine(subroutine: Subroutine, params: HList): Any = {
subroutine match {
case Curried(partiallyApplied, unapplied) =>
partiallyApplied(params.head)(executeSubroutine(unapplied, params.tail))
case Uncurried(function) => function(params.head)
}
}
If anyone can help me out that would be greatly appreciated!
-
\$\begingroup\$ (Welcome to CR!) "The norm is to summarize the goal of the code in the title." \$\endgroup\$greybeard– greybeard2018年03月06日 07:05:07 +00:00Commented Mar 6, 2018 at 7:05
-
\$\begingroup\$ So to clarify, you have a working implementation that you would like reviewed, and another non-working implementation for reference? \$\endgroup\$Dan Oberlam– Dan Oberlam2018年04月16日 20:25:55 +00:00Commented Apr 16, 2018 at 20:25
1 Answer 1
Unless I misunderstand your question, I think this is what you want:
type Subroutine[Param] = Param => Any
type Routine = Seq[(Subroutine[_], Seq[Any])]
def execRoutine(routine: Routine): Unit = routine foreach { case (sub, params) => execSubroutine(sub, params) }
def execSubroutine[SubParam] (subroutine: Subroutine[SubParam], params: Seq[Any]): Unit = {
require(params.nonEmpty)
subroutine(params.head.asInstanceOf[SubParam]) match {
case sub: Subroutine[_] => execSubroutine(sub, params.tail)
case () => require(params.tail.isEmpty) // Require correct number of params.
case _ => throw new IllegalStateException()
}
}
val add2 = (a: Int) => (b: Int) => println(a + b)
val add3 = (a: Int) => (b: Int) => (c: Int) => println(a + b + c)
execRoutine(List(
(add2, List(3, 4)),
(add3, List(3, 4, 5))
))
This program prints 7 and 12. Unfortunately, if the parameters are of the wrong type, a runtime exception occurs instead of a compile time one. Attempting to apply the less or more than the required parameters also results in a runtime exception.
-
2\$\begingroup\$ So does the OP's cod enot work, or yours? \$\endgroup\$Dan Oberlam– Dan Oberlam2018年04月16日 01:14:38 +00:00Commented Apr 16, 2018 at 1:14
-
\$\begingroup\$ @Dannnno The OP's code does not work, as they stated, but my code does compile and run correctly. Does that address your question? \$\endgroup\$Llew Vallis– Llew Vallis2018年04月16日 02:59:24 +00:00Commented Apr 16, 2018 at 2:59