This document discusses porting a Java library for distributed object coordination called Fly to Scala. The port involved syntactic conversion to Scala, using more idiomatic Scala APIs, adopting Scala idioms internally, leveraging actors, and comparing Java and Scala. Overall, the Scala version was 60% shorter due to better abstractions for common algorithms. Future work could include implementing the library from scratch with a more functional design.
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Porting Java To Scala
1. >
<----
--------->
<----
>
DEVELOPING THE SCALA BINDINGS FOR
THE FLY OBJECT SPACE
Channing Walton
Nigel Warren
channing.walton@casualmiracles.com nige@zink-digital.com
http://www.casualmiracles.com/
CM, Java experience, CRE, UCL
Presentation about my experience porting from Java to Scala
3. SPACES
Tuple Spaces - Linda (a coordination language)
in - put a tuple into a space
rd – get a copy of a tuple from the space
out – remove tuple from the space
Subsequently …
Java Spaces - Jini – Java
Rinda – Ruby
PyLinda – Python
etc., etc.
http://www.casualmiracles.com/
4. FLY
Fly is an ‘Object Space’
Network wide - Object level - Storage and Matching
Matching – by Object Templates
Object Serialisation – Native to a language or language neutural
Leasing – Information and resources have prescribed lifetimes
http://www.casualmiracles.com/
5. FLY PRIME INTERFACE
public interface FlyPrime {
long
write(Object entry, long leaseTime);
Object
read(Object template, long waitTime);
Object
take(Object template, long waitTime);
}
http://www.casualmiracles.com/
7. FLY SPACE DESIGN
Minimal But Complete Interface Write
Read
Take
Threading
Object Locking
Distribution
Matching
Expiring
Pthreads
Minimal Use of Sockets
OS interfaces Malloc - Free
http://www.casualmiracles.com/
8. FLY SPACE INTERFACE HIERARCHY
Fly
MultiFly NotiFly
writeMany notifyWrite
readMany notifyTake
takeMany
FlyPrime
write
read
take
http://www.casualmiracles.com/
9. FLY SCALA
Scala Application Scala Application
Client Client
Scala Space
Scala Space Interface
Interface
Scala Binding Scala Binding
Fly Server
http://www.casualmiracles.com/
10. FROM JAVA TO SCALA
• Syntactic Conversion
• Idiomatic API
• Idioms Internally
• Actors
• Java and Scala Compared
• What Next?
http://www.casualmiracles.com/
12. SYNTACTIC
public static void main(String[] args) throws Exception { def main(args: Array[String]) {
FileInputStream f = new FileInputStream(new File(args[0])); val f = new FileInputStream(new File(args(0)));
DataInputStream dis = new DataInputStream(f); val dis = new DataInputStream(f);
StatsDecoder decoder = new StatsDecoder(); val decoder = new StatsDecoder();
long time = dis.readLong(); var time = dis.readLong();
while ( true ) { while (true) {
int size = dis.readInt(); val size = dis.readInt();
StatsBean [] beans = decoder.getStatsArray(dis); val beans = decoder.getStats(dis);
Stats.writeStats(beans); StatsPrinter.writeStats(beans);
System.out.println("--------------"); System.out.println("--------------");
long nextTime = dis.readLong(); val nextTime = dis.readLong();
Thread.sleep(nextTime-time); Thread.sleep(nextTime - time);
time = nextTime; time = nextTime;
} }
} }
http://www.casualmiracles.com/
13. IDIOMS - OPTION
An Option represents an optional value
Two subclasses: Some and None
Java has … null
http://www.casualmiracles.com/
Experiment with Options in Java later
14. IDIOMS - OPTION
trait FlyPrime {
def read[T <: AnyRef](template: T, waitTime: Long): Option[T]
}
fly.read(template, 0L) match {
case None => {
println("No ball in play")
serveBall(fly)
println("Served Ball - Please start a Pong")
}
case Some(gameBall) => {
println("Received ball - game on!")
returnBall(fly, gameBall)
}
}
http://www.casualmiracles.com/
pattern matching - scala can extract values from the matching pattern -
Some(ball)
no types
15. IDIOMS - OPTION
trait FlyPrime {
def read[T <: AnyRef](template: T, waitTime: Long): Option[T]
}
fly.read(template, 0L) match {
case None => // do nothing
case Some(gameBall) => doSomething(gameBall)
}
fly.read(template, 0L).map((x:T) => doSomething(x))
fly.read(template, 0L).map(doSomething(_))
http://www.casualmiracles.com/
do nothing for none - verbose to use pattern matching
(x:T) => doSomething(x) is a first class function
16. ASIDE
Options can be implemented in other languages
Clumsy in Java but still useful
An experiment:
Problems exposed where null was not expected
Clarify business logic and behaviour
http://www.casualmiracles.com/
17. IDIOMS - RETURN VALUES
int iterations = 10000;
if (args.length > 0) iterations = Integer.parseInt(args[0]);
val iterations = if (args.length > 0) args(0).toInt else 10000
def urlFor(path: String) =
try {
new URL(path)
} catch {
case e: MalformedURLException => new URL("http://www.scala-lang.org")
}
http://www.casualmiracles.com/
pattern matching for exceptions
exceptions are runtime
18. IDIOMS - FILTERING
public Collection<FlyServerRep> getMatchingReps(String [] tags) {
Collection matched = new ArrayList<FlyServerRep>();
for (FlyServerRep rep : reps.values()) {
if (rep.tagsMatch(tags)) {
matched.add(rep);
}
}
return matched;
}
def getMatchingReps(tags:Array[String]):Collection[FlyServerRep]
= reps.values.filter((rep:FlyServerRep) => rep.tagsMatch(tags)).toList
def getMatchingReps(tags:Array[String]):Collection[FlyServerRep]
= reps.values.filter(_.tagsMatch(tags)).toList
http://www.casualmiracles.com/
filter items from a collection
19. IDIOMS - COMPREHENSIONS
public StatsBean[] getStatsArray(DataInputStream dis) throws IOException {
long statsCount = dis.readLong();
StatsBean [] stats = new StatsBean[(int)statsCount];
for (int i = 0; i < statsCount; i++) {
stats[i] = StatsBean.makeBeanFromStream(dis);
}
return stats;
}
def getStats(dis: DataInputStream): Seq[StatsBean] =
for (i <- 0 until dis.readLong().toInt) yield StatsBean.makeBeanFromStream(dis)
http://www.casualmiracles.com/
Create an array of items
for comprehension - iterate over something collecting results of the
expression after yield
20. FOR COMPREHENSION
Syntax: for ( seq ) yield e
Where seq is a sequence of generators, definitions and filters
e evaluated for each binding of generators and definitions
Return a sequence of evaluated values
http://www.casualmiracles.com/
generator— roughly speaking, an expression that pulls an item from a
collection
easier to show an example...
21. FOR COMPREHENSION
val names = for {
p <- persons // a generator
n = p.name // a definition
if (n startsWith "To") // a filter
} yield n
http://www.casualmiracles.com/
p bound to each item in persons
22. IDIOMS - FOLD
/**
* @return the lease of the last entry written
*/
public long writeMany(Collection entries, long lease) {
long lastLease = 0;
for (Object entry : entries) {
lastLease = codec.write( entry, lease );
}
return lastLease;
}
def writeMany(entries: Collection[AnyRef], lease: Long): Long
= (0L /: entries){(previousLease, nextEntry) => codec.write(nextEntry, lease)}
http://www.casualmiracles.com/
method writes entries to the space and returns the lease of the last item
written
function takes two parameters, ignores previousLease, returns the result of
codec.write
good use of fold?
23. IDIOMS - ACCESSORS
public class FlyServerRep { class FlyServerRep(var flyAddr:InetAddress, var flyTags:Array[String])
private String [] flyTags;
private InetAddress flyAddr;
FlyServerRep(InetAddress addr, String[] tags) {
flyAddr = addr;
flyTags = tags;
}
public String[] getFlyTags() {
return flyTags;
}
public void setFlyTags(String[] flyTags) {
this.flyTags = flyTags;
}
public InetAddress getFlyAddr() {
return flyAddr;
}
public void setFlyAddr(InetAddress flyAddr) {
this.flyAddr = flyAddr;
}
}
http://www.casualmiracles.com/
var on constructor parameters, scala generates accessor methods: x and x_
24. IDIOMS - FUNCTIONS
public Object read(Object template, long timeout) { public Object take(Object template, long timeout) {
Object ret = codec.read(template, 0); Object ret = codec.take(template, 0);
.... ....
while(...) while(...)
ret = codec.read(template, 0); ret = codec.take(template, 0);
def read[T <: AnyRef](template: T, timeout: Long): Option[T] = retrieve(template, timeout, codec.read)
def take[T <: AnyRef](template: T, timeout: Long): Option[T] = retrieve(template, timeout, codec.take)
private def retrieve[T](template: T, timeout: Long, m: (T, Long) => Option[T]): Option[T] = {
var ret = m(template, 0L)
....
while(...)
ret = m(template, 0)
http://www.casualmiracles.com/
25. CONTROL ABSTRACTION
System.out.println("Processing " + iterations + " writes and reads");
long start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
space.write(object, 1000);
space.read(template, 0L);
}
long end = System.currentTimeMillis();
float timeInSeconds = (float) (end - start) / 1000.0f;
System.out.println("Which took " + timeInSeconds + " secondsn");
http://www.casualmiracles.com/
26. CONTROL ABSTRACTION
Time("Processing " + iterations + " writes and takes", iterations) {
space.write(obj, 1000)
space.take(template, 0L)
}
object Time {
def apply(name: String, iterations: Int)(block: => Unit): Unit = {
println(name)
val start = System.currentTimeMillis()
for (i <- 0 until iterations) block
val end = System.currentTimeMillis()
val timeInSeconds = (end - start) / 1000.0F
println("Completed in " + timeInSeconds + " secondsn")
}
}
http://www.casualmiracles.com/
apply methods have special meaning - invoked using a method-less
expression
eg Time (... is the same as Time.apply(...
28. ACTORS
Scala’s primary concurrency construct
Concurrent processes communicating by exchanging messages
Scala Actor implementation is a library - several of them
http://www.casualmiracles.com/
29. ACTORS
trait NotiFly extends FlyPrime {
def notifyWrite(template: AnyRef, leaseTime: Long, actor: Actor): Boolean
}
import scala.actors.Actor._
val myActor = actor { // factory method taking a block
loop { // we want the actor to keep processing messages
react { // handle messages
case FlyPrime.ACTOR_MESSAGE => println("Actor received a message!")
}
}
}
fly.notifyWrite(template, LEASE, myActor)
http://www.casualmiracles.com/
30. JAVA TO SCALA COMPARED
Java has 1556 LoC, Scala has 934 LoC => 60%
Core code is about half the size
Why?
Most algorithms can be characterised as Searching, Sorting, Filtering,
Mapping, Combining, Counting (Peter Norvig)
Scala and functional languages provide good abstractions for these
Java is 2% faster
Need more comprehensive tests to find out why
http://www.casualmiracles.com/
31. WHAT NEXT?
Scala 2.8
Port from 2.7.7 complete
Available for 2.8 soon
Implement the library from scratch?
Design of existing library is the same as the Java library
Current version made improvements in the small
Perhaps a more functional approach would be better?
http://www.casualmiracles.com/
32. >
<----
--------->
<----
>
FROM JAVA TO SCALA
http://www.casualmiracles.com/
2/3 lines of code
could be better