Interlanguage Working Without Tears: Blending SML with Java

skatechildrenΛογισμικό & κατασκευή λογ/κού

3 Νοε 2013 (πριν από 3 χρόνια και 7 μήνες)

55 εμφανίσεις

Interlanguage Working
Without Tears:

Blending SML with Java

Andrew Kennedy

Nick Benton

Microsoft Research Cambridge

Goal


Fun for functional programmers:


GUIs, 3
-
d, sound, video, email, crypto,
imaging, server
-
side code, phone, TV, …


Achieved by an interface between SML and
Java.


Implemented in MLj, a compiler that
generates Java class files from SML’97
source.

Three approaches to interop

1.
Bilateral interface with marshalling and explicit
calling conventions (e.g. JNI, O’Caml interface
for C).

2.
Multilateral interface with IDL (e.g. COM,
CORBA) together with particular language
mappings (e.g. H/Direct, Caml COM,
MCORBA).

3.
Language integration (MLj).

1. Explicit Bilateral Interface


Two languages have distinct type systems and calling
conventions.


Interface by:


Marshalling data between “compatible” types (e.g.
java.lang.String

to
const char*

by copying). Often
restricted to a subset of the type system.


Giving directives for exporting and importing functions with
language
-
specific calling conventions (e.g.
_pascal _cdecl
).


Usually tied to particular compiler implementations (e.g.
SML/NJ and MLWorks have different C interfaces).


Realistically used only by experts.

Example: JNI


JNIEXPORT

jstring
JNICALL

Java_Prompt_getLine(JNIEnv *env,
jobject obj, jstring prompt)

{


char buf[128];


const char *str = (*env)
-
>
GetStringUTFChars
(env,prompt,0);




printf("%s", str);


(*env)
-
>ReleaseStringUTFChars(env, prompt, str);





scanf("%s", buf);


return (*env)
-
>
NewStringUTF
(env, buf);

}

2. IDL
-
based interop


Idea:


Use a language
-
independent interface definition
language (IDL) to describe the signatures of functions
that are to be called across the border.


Generate stub code using a language
-
specific tool.


Good because it separates the interface from
the language and supports multilateral interop.


But: the programmer has to write IDL code.

3. Our approach: Integration


Idea:


When the “semantic gap” between two languages is
small, integrate features of one language into the
other.


If done well, can be used by novices.


But: language (and perhaps implementation)
specific.

Our languages: SML & Java


Both languages are strongly typed with good
correspondences:


Numeric types match closely


Strings are immutable vectors


Arrays have run
-
time sizing and bounds checking


Neither language has explicit pointer types


Both languages have automatic storage
management.


Exception handling in both languages is similar.


But: there are significant differences too.

Interop in MLj 0.1


The bolt
-
it
-
on approach:

SML

Java

Interop in MLj 0.1


Name Java types (Java.int, “java.util.Vector”) and provide
coercions between ML and Java types (e.g. Java.fromInt,
Java.toInt)


Provide new constructs corresponding to some Java
language constructs (in fact, often closer to JVM
bytecodes)

MLj 0.1


The bolt
-
it
-
on approach:

Example


_public _method "handleEvent" (e : Event option) : Java.boolean =


if Java.toInt(_getfield "id" (valOf e)) =


Java.toInt(_getfield Event "WINDOW_DESTROY")


then


OS.Process.terminate OS.Process.success


else



_invoke "handleEvent" (_super, e)


Response from some users:

Ugh
!

New design for MLj


The blending approach:

SML

Java

New design for MLj


The blending approach:

MLj 1.0


Don’t just attempt to replicate Java constructs


Instead:


re
-
use SML concepts where appropriate


invent clean new syntax elsewhere


Design goals


Simplicity


Lightweight syntax


Easy to convert Java code into MLj


Compatibility:


SML’97 programs typecheck and run without change


Safety:


Java
-
style type safety + avoid NullPointerException


Power:


Improve on Java where possible

A non
-
goal


To pass ML
-
specific values into Java


It’s less useful


write code in MLj instead


It could compromise safety (e.g. by mutating
ML values)


It requires uniform data representations, but
we want the chance to optimise the
representations


Example code

open javax.swing java.awt java.awt.event

_classtype SampleApplet () : JApplet ()

with local


val prefix = "Counter: “


val count = ref 0


val label = JLabel (prefix ^ "0", JLabel.CENTER)


fun makeButton (title, increment) =


let val button = JButton (title:string)


val listener =

ActionListener ()


with


actionPerformed (e : ActionEvent option) =


(count := !count + increment;


label.#setText(prefix ^ Int.toString (!count)))

end


in


button.#addActionListener(listener);


button


end

in


init () =


let

val SOME pane = this.#getContentPane ()



val button1 = makeButton ("Add One", 1)



val button2 = makeButton ("Add Two", 2)


in pane.#add(button1, BorderLayout.WEST);



pane.#add(label, BorderLayout.CENTER);




pane.#add(button2, BorderLayout.EAST)



end

end

Analogies between ML and Java

static field

static method

package

void

null

multiple args

mutability

non
-
static methods

ref

open

unit

structure

NONE

val binding

fun binding

tuple

type identifier

Java

SML

class name

import

casts

instanceof

private fields

local decs

class defs

non
-
static fields

Types

Java type

ML type

boolean

bool

byte

Int8.int

char

char

double

real

float

Real32.real

int

int

long

Int64.int

short

Int16.int

java.lang.String

string

java.lang.Exception

exn

java.math.BigInteger

IntInf.int

java.util.Calendar

Date.date

X[]

X array

Null values


Java
reference

values (arrays & objects)
can take the value
null


ML doesn’t have this notion, so values of
array and class types are interpreted as
“non
-
null instance”


Then


datatype ‘a option = NONE | SOME of ‘a

is used for possibly
-
null objects and arrays

Fields and methods


Final fields (Java’s “const”) = ML values


Non
-
final fields = ML refs


Methods are given function types with


Tuples for multiple args


Unit for void arg and result


Implicit Java
-
style casts on arguments + T to
T option

Fields and methods, cont.


Static fields & methods are just bindings in ML structures
(= Java class) embedded in a hierarchy of structures (=
Java packages)


Non
-
static members are accessed through .# notation


Constructors are just bindings with the same name as
the type (= Java
new C
)


Improving on Java: first
-
class fields and methods e.g.


val colours = map (valOf o java.awt.Color.getColor)
[“red”,“green”]


val labels = map javax.swing.JLabel [“ICFP”,”PLI”]


Casts and typecase


Java
-
style upcasts, using Caml
-
like syntax


val c = Jbutton (“My button”) :> Component


Also used for downcasts, but neater alternative
is “cast patterns”:



case (e : Expr) of


ce :> CondExpr => …


| ae :> AssignExpr => …

Creating Java classes in ML

1.
Export

an ML structure as a class, with
functional values interpreted as static
methods, non
-
functional values interpreted as
static fields

2.
New
_classtype

construct

Example

_classtype Point(xinit, yinit)

With local


val x = ref xinit


val y = ref yinit

in


getX() = !x


and getY() = !y


and move(xinc,yinc) = (x := !x+xinc; y := !y+yinc)


and moveHoriz xinc = this.#move(0, yinc)


and moveVert yinc = this.#move(xinc, 0)

end

Example


Single
constructor
, with args used throughout definition
(as in O’Caml)


No fields! (Instead, use
local definitions
)


Use of
this

as in Java

_classtype
Point(xinit, yinit)

With local


val x = ref xinit


val y = ref yinit

in


getX() = !x


and getY() = !y


and move(xinc,yinc) = (x := !x+xinc; y := !y+yinc)


and moveHoriz xinc =
this
.#move(0, yinc)


and moveVert yinc =
this
.#move(xinc, 0)

end

Example, cont.

_classtype ColouredPoint(x,y,c) : Point(x,y)

with


getColour() = c : java.awt.Color


and move (xinc,yinc) = this.##move(xinc*2, yinc*2)

end

Example, cont.

_classtype ColouredPoint(x,y,c) :
Point
(x,y)

with


getColour() = c : java.awt.Color


and
move
(xinc,yinc) = this
.##
move(xinc*2, yinc*2)

end


Superclass

specified with
arguments

to its constructor


Overriding

of methods


Special
syntax

for superclass method invocation

Finale


Classic functional techniques:


Backtracking & lazy lists to solve Eight
Queens


Combinators for music (
à

la Hudak)


Interpreted using Java multimedia
libraries…

Conclusion


Language interop is hard to get right


it’s a language
design problem like any other


We think we’ve done a good job!


See the paper for formalisation in the style of the
Definition of Standard ML


Main line of future work: better
inference


Currently, some programs with unique typings are rejected
because types are inferred on
-
the
-
fly


Instead, first do pass over term generating constraints, then
solve them.


Available soon in MLj


for now, see


http://www.dcs.ed.ac.uk/~mlj