Scala API Extensions

Scala API Extensions #

In order to keep a fair amount of consistency between the Scala and Java APIs, some of the features that allow a high-level of expressiveness in Scala have been left out from the standard APIs for both batch and streaming.

If you want to enjoy the full Scala experience you can choose to opt-in to extensions that enhance the Scala API via implicit conversions.

To use all the available extensions, you can just add a simple import for the DataStream API

import org.apache.flink.streaming.api.scala.extensions._

Alternatively, you can import individual extensions a-là-carte to only use those you prefer.

Accept partial functions #

Normally, the DataStream API does not accept anonymous pattern matching functions to deconstruct tuples, case classes or collections, like the following:

val data: DataStream[(Int, String, Double)] = // [...]
data.map {
  case (id, name, temperature) => // [...]
  // The previous line causes the following compilation error:
  // "The argument types of an anonymous function must be fully known. (SLS 8.5)"
}

This extension introduces new methods in the DataStream Scala API that have a one-to-one correspondence in the extended API. These delegating methods do support anonymous pattern matching functions.

DataStream API #

Method Original Example
mapWith map (DataStream)
data.mapWith {
  case (_, value) => value.toString
}
flatMapWith flatMap (DataStream)
data.flatMapWith {
  case (_, name, visits) => visits.map(name -> _)
}
filterWith filter (DataStream)
data.filterWith {
  case Train(_, isOnTime) => isOnTime
}
keyingBy keyBy (DataStream)
data.keyingBy {
  case (id, _, _) => id
}
mapWith map (ConnectedDataStream)
data.mapWith(
  map1 = case (_, value) => value.toString,
  map2 = case (_, _, value, _) => value + 1
)
flatMapWith flatMap (ConnectedDataStream)
data.flatMapWith(
  flatMap1 = case (_, json) => parse(json),
  flatMap2 = case (_, _, json, _) => parse(json)
)
keyingBy keyBy (ConnectedDataStream)
data.keyingBy(
  key1 = case (_, timestamp) => timestamp,
  key2 = case (id, _, _) => id
)
reduceWith reduce (KeyedStream, WindowedStream)
data.reduceWith {
  case ((_, sum1), (_, sum2) => sum1 + sum2
}
projecting apply (JoinedStream)
data1.join(data2).
  whereClause(case (pk, _) => pk).
  isEqualTo(case (_, fk) => fk).
  projecting {
    case ((pk, tx), (products, fk)) => tx -> products
  }

For more information on the semantics of each method, please refer to the DataStream API documentation.

To use this extension exclusively, you can add the following import:

import org.apache.flink.api.scala.extensions.acceptPartialFunctions

for the DataSet extensions and

import org.apache.flink.streaming.api.scala.extensions.acceptPartialFunctions

The following snippet shows a minimal example of how to use these extension methods together (with the DataSet API):

object Main {
  import org.apache.flink.streaming.api.scala.extensions._

  case class Point(x: Double, y: Double)

  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val ds = env.fromElements(Point(1, 2), Point(3, 4), Point(5, 6))
    
    ds.filterWith {
      case Point(x, _) => x > 1
    }.reduceWith {
      case (Point(x1, y1), (Point(x2, y2))) => Point(x1 + y1, x2 + y2)
    }.mapWith {
      case Point(x, y) => (x, y)
    }.flatMapWith {
      case (x, y) => Seq("x" -> x, "y" -> y)
    }.keyingBy {
      case (id, value) => id
    }
  }
}

Back to top