All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.touk.nussknacker.engine.api.LazyParameter.scala Maven / Gradle / Ivy

There is a newer version: 1.18.0
Show newest version
package pl.touk.nussknacker.engine.api

import pl.touk.nussknacker.engine.api.LazyParameter.{Evaluate, MappedLazyParameter, ProductLazyParameter}
import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult}

import scala.reflect.runtime.universe.TypeTag

/**
  * Lazy parameter is representation of parameter of custom node which should be evaluated for each record:
  * ```def execute(@ParamName("groupBy") groupBy: LazyParameter[String], @ParamName ("length") length: String)```
  * In this case, length is computed as constant during process compilation, while groupBy is evaluated for each event
  * Cannot be evaluated directly (no method like 'evaluate'),
  * as evaluation may need lifecycle handling, to use it see LazyParameterInterpreter
  *
  * @tparam T type of evaluated parameter. It has upper bound AnyRef because currently we don't support correctly extraction of
  *          primitive types from generic parameters
  */
// TODO: rename to TypedFunction
trait LazyParameter[+T <: AnyRef] extends Serializable {

  def evaluate: Evaluate[T]

  // type of parameter, derived from expression. Can be used for dependent types, see PreviousValueTransformer
  def returnType: TypingResult

  // we provide only applicative operation, monad is tricky to implement (see CompilerLazyParameterInterpreter.createInterpreter)
  // we use product and not ap here, because it's more convenient to handle returnType computations
  def product[B <: AnyRef](fb: LazyParameter[B]): LazyParameter[(T, B)] =
    new ProductLazyParameter(this, fb)

  def map[Y <: AnyRef: TypeTag](fun: T => Y): LazyParameter[Y] =
    map(fun, _ => Typed.fromDetailedType[Y])

  // unfortunately, we cannot assert that TypingResult represents Y somehow...
  def map[Y <: AnyRef](fun: T => Y, transformTypingResult: TypingResult => TypingResult): LazyParameter[Y] =
    new MappedLazyParameter[T, Y](this, fun, transformTypingResult)

}

object LazyParameter {

  type Evaluate[+T] = Context => T

  // Sequence requires wrapping of evaluation result and result type because we don't want to use heterogeneous lists
  def sequence[T <: AnyRef, Y <: AnyRef](
      fa: List[LazyParameter[T]],
      wrapResult: List[T] => Y,
      wrapReturnType: List[TypingResult] => TypingResult
  ): LazyParameter[Y] =
    new SequenceLazyParameter(fa, wrapResult, wrapReturnType)

  // Name must be other then pure because scala can't recognize which overloaded method was used
  def pureFromDetailedType[T <: AnyRef: TypeTag](value: T): LazyParameter[T] =
    new FixedLazyParameter(value, Typed.fromDetailedType[T])

  def pure[T <: AnyRef](value: T, valueTypingResult: TypingResult): LazyParameter[T] =
    new FixedLazyParameter(value, valueTypingResult)

  def product[T <: AnyRef, Y <: AnyRef](arg1: LazyParameter[T], arg2: LazyParameter[Y]): LazyParameter[(T, Y)] =
    new ProductLazyParameter(arg1, arg2)

  def mapped[T <: AnyRef, Y <: AnyRef](
      lazyParameter: LazyParameter[T],
      fun: T => Y,
      transformTypingResult: TypingResult => TypingResult
  ): LazyParameter[Y] =
    new MappedLazyParameter[T, Y](lazyParameter, fun, transformTypingResult)

  trait CustomLazyParameter[+T <: AnyRef] extends LazyParameter[T]

  final class ProductLazyParameter[T <: AnyRef, Y <: AnyRef](
      val arg1: LazyParameter[T],
      val arg2: LazyParameter[Y]
  ) extends LazyParameter[(T, Y)] {

    override val returnType: TypingResult = Typed.genericTypeClass[(T, Y)](List(arg1.returnType, arg2.returnType))

    override val evaluate: Evaluate[(T, Y)] = {
      val arg1Evaluator = arg1.evaluate
      val arg2Evaluator = arg2.evaluate
      ctx: Context => (arg1Evaluator(ctx), arg2Evaluator(ctx))
    }

  }

  final class SequenceLazyParameter[T <: AnyRef, Y <: AnyRef](
      val args: List[LazyParameter[T]],
      val wrapResult: List[T] => Y,
      val wrapReturnType: List[TypingResult] => TypingResult
  ) extends LazyParameter[Y] {

    override val returnType: TypingResult =
      wrapReturnType(args.map(_.returnType))

    override val evaluate: Evaluate[Y] = {
      val argsEvaluators = args.map(_.evaluate)
      ctx: Context => wrapResult(argsEvaluators.map(_.apply(ctx)))
    }

  }

  final class MappedLazyParameter[T <: AnyRef, Y <: AnyRef](
      val arg: LazyParameter[T],
      val fun: T => Y,
      val transformTypingResult: TypingResult => TypingResult
  ) extends LazyParameter[Y] {

    override val returnType: TypingResult = transformTypingResult(arg.returnType)

    override val evaluate: Evaluate[Y] = {
      val argEvaluator = arg.evaluate
      ctx: Context => fun(argEvaluator.apply(ctx))
    }

  }

  final class FixedLazyParameter[T <: AnyRef](value: T, override val returnType: TypingResult)
      extends LazyParameter[T] {

    override val evaluate: Evaluate[T] = _ => value
  }

}

// This class is Flink-specific. It allows to evaluate value of lazy parameter in case when LazyParameter isn't
// a ready to evaluation function. In Flink case, LazyParameters are passed into Flink's operators so they
// need to be Serializable. Because of that they can't hold heavy objects like ExpressionCompiler or ExpressionEvaluator
trait ToEvaluateFunctionConverter {

  def toEvaluateFunction[T <: AnyRef](parameter: LazyParameter[T]): Evaluate[T]

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy