The flow is an interesting serial port library
- Not RxTx (whose replacement is long overdue)
- Uses Akka IO
The native code infrastructure is also interesting and might be useful in other projects. That will require additional study.
The API has a surprising wrinkle, that might be worthy of further discussion.
Serial.scala has a method
case class Write(data: ByteString, ack: Int => Event = NoAck) extends Command
case object NoAck extends Function1[Int, Event] {
def apply(length: Int) = sys.error("cannot apply NoAck")
}
The sys.error means NoAck(12)
would throw a RuntimeException.
That obviously does not occur and further investigation turns up this implementation
if (ack != NoAck) sender ! ack(sent)
This is essentially a null pointer check, which would not generally be considered Idiomatic Scala.
After some experience with Scala, writing if
is starting to feel as inappropriate as writing goto
.
NoAck is also an example of the “uninherit” anti-pattern, where the derived implementation attempts to ignore the code provided by the base class.
The Java equivalent would throw UnsupportedOperationException
.
The canonical Scala (and Java 8) solution would use Option
case class Write(data: ByteString, ack: Option[Int => Event] = None) extends Command
or
case class Write(data: ByteString, ack: Int => Option[Event] = NoAck) extends Command
case object NoAck extends Function1[Int, Option[Event]] {
def apply(length: Int) = None
}
The if test might have better performance, but that is unlikely to be a concern for code that drives a serial port.
Consider the usage of ack: Int => Event
.
The sample Terminal.scala has this usage
case class Wrote(data: ByteString) extends Event
...
case Wrote(data) => log.info(s"Wrote data: ${formatData(data)}")
...
val data = ByteString(input.getBytes)
operator ! Write(data, length => Wrote(data.take(length)))
Which means data
is referenced in the receive
method of the operator
actor.
In this case, there is no problem because data
is immutable.
Note that ack
is only useful if it closes over state, which will require case to ensure well defined behavior.