You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: papers/icwe14/icwe14.tex
+64-29Lines changed: 64 additions & 29 deletions
Original file line number
Diff line number
Diff line change
@@ -70,7 +70,7 @@
70
70
\maketitle
71
71
72
72
\begin{abstract}
73
-
73
+
Encoding dynamically typed APIs in statically typed languages can be challenging. We present new ways to encode them in order to preserve type safety and to keep the same expression power.
74
74
\end{abstract}
75
75
76
76
\section{Introduction}
@@ -126,7 +126,8 @@ \subsection{The browser API and its integration in statically typed languages}
126
126
\end{lstlisting}
127
127
\end{figure}
128
128
129
-
The case of \jscode{target.addEventListener(name, listener)} is a bit different. The \jscode{name} parameter defines the event to listen to and the \jscode{listener} parameter the function to call back each time such an event occurs. Instead of being polymorphic in its return type, it is polymorphic in its \jscode{listener} parameter. Nevertheless, a similar property as above holds: there is exactly one possible type for the \jscode{listener} parameter for each value of the \jscode{name} parameter. For instance, a listener of \jscode{'click'} events is a function taking a \jscode{MouseEvent} parameter, a listener of \jscode{'keydown'} events is a function taking a \jscode{KeyboardEvent} parameter, and so on. The same pattern as above (defining a set of functions fixing the \jscode{name} parameter value) can be used to encode this function in a statically typed language, but an alternative encoding could be to define one function taking one parameter carrying both the information of the event name and the event listener. Listing~\ref{lst-add-event-listener-one-param} shows such an encoding in Java.
129
+
The case of\jscode{target.addEventListener(name, listener)} is a bit different. The\jscode{name} parameter defines the event to listen to and the\jscode{listener} parameter the function to call back each time such an event occurs. Instead of being polymorphic in its return type, it is polymorphic in its\jscode{listener} parameter. Nevertheless, a similar property as above holds: there is exactly one possible type for the\jscode{listener} parameter for each value of the\jscode{name} parameter. For instance, a listener of\jscode{'click'} events is a function taking a\jscode{MouseEvent} parameter, a listener of\jscode{'keydown'} events is a function taking a\jscode{KeyboardEvent} parameter, and so on. The same pattern as above (defining a set of functions fixing the\jscode{name} parameter value) can be used to encode this function in a statically typed language, but an alternative encoding could be to define one function taking one parameter carrying both the information of the event name and the
130
+
event listener. Listing~\ref{lst-add-event-listener-one-param} shows such an encoding in Java.
130
131
131
132
\begin{figure}
132
133
\begin{lstlisting}[label=lst-add-event-listener-one-param,language=Java,caption={Encoding of the \jscode{target.addEventListener(name, listener)} function in Java using one paremeter carrying both the information of the event name and the event listener}]
\item Use type\scalacode{U} instead of type\scalacode{T}
228
229
\end{enumerate}
229
230
230
-
Listing~\ref{lst-generics-dom} shows this approach applied to the\jscode{createElement} function: we created a type\scalacode{ElementName<E>}, the\scalacode{name} parameter is not a\scalacode{String} but a\scalacode{ElementName<E>}, and the function returns an\scalacode{E} instead of an\scalacode{Element}. The\scalacode{ElementName<E>} type encodes the relationship between the name of an element and the type of this element. For instance, we created a value\scalacode{Input} of type\scalacode{ElementName<InputElement>}. The last two lines shows how this API is used by end developers: by passing the\scalacode{Input} value as parameter to the function, it fixes the type parameter\scalacode{E} to\scalacode{InputElement} so the returned value has the most possible precise type.
231
+
Listing~\ref{lst-generics-dom} shows this approach applied to the\jscode{createElement} function: we created a type\scalacode{ElementName<E>}, the\scalacode{name} parameter is not a\scalacode{String} but a\scalacode{ElementName<E>}, and the function returns an\scalacode{E} instead of an\scalacode{Element}. The\scalacode{ElementName<E>} type encodes the relationship between the name of an element and the type of this element\footnote{The type parameter\scalacode{E} is also called a\emph{phantom type}~\cite{leijen1999domain} because\scalacode{ElementName} values never hold a\scalacode{E} value}. For instance, we created a value\scalacode{Input} of type\scalacode{ElementName<InputElement>}. The last two lines shows how this API is used by end developers: by passing the\scalacode{Input} value as parameter to the function, it fixes the type parameter\scalacode{E} to\scalacode{InputElement} so the returned value has the most possible precise type.
We show that our encodings support the challenging functions defined in listings\ref{lst-js-comb} and\ref{lst-js-react}.
270
+
271
+
Listing\ref{lst-generics-comb} and\ref{lst-generics-react} show how these functions can be implemented. They are basically a direct translation from JavaScript to Java.
Our encoding makes it possible to implement exactly the same functions as we are able to implement in plain JavaScript, but with type safety.
296
+
297
+
However, every function taking an element name or an event name as parameter must have type parameters too, making its type signature harder to read.
298
+
267
299
\subsection{Path-Dependent Types}
268
300
301
+
This section shows how we can remove the extra type parameters needed in the previous section by using\emph{path-dependent types}. Essentially, the idea is that, instead of using a type parameter, we use a type member. Listings\ref{lst-dt-dom} and\ref{lst-dt-events} show this encoding in Scala. The return type of the\scalacode{createElement} function is\scalacode{name.Element}: it refers to the\scalacode{Element} type member of its\scalacode{name} parameter.
302
+
269
303
\begin{figure}
270
304
\begin{lstlisting}[label=lst-dt-dom]
271
305
trait ElementName {
272
306
type Element
273
307
}
308
+
object Div extends ElementName { type Element = DivElement }
309
+
object Input extends ElementName { type Element = InputElement }
object Click extends EventDef { type Event = MouseEvent }
288
322
289
-
val clicks = on(Click)
290
-
clicks(event => println(event.offsetX))
323
+
trait EventTarget {
324
+
def addEventListener(name: EventName)(handler: name.Event => Unit): Unit
325
+
}
291
326
\end{lstlisting}
292
327
\end{figure}
293
328
294
-
\section{Validation}
295
-
\label{sec-validation}
296
-
297
-
We show that our encodings support the challenging functions defined in listings\ref{lst-js-comb} and\ref{lst-js-react}.
298
-
299
-
Listing\ref{lst-generics-comb} and\ref{lst-generics-react} show how these functions can be implemented. They are basically a direct translation from JavaScript to Java.
329
+
Implementing listings\ref{lst-js-comb} and\ref{lst-js-react} using this encoding is straightforward, as shown by listings\ref{lst-dt-comb} and\ref{lst-dt-react}, respectively.
With this encoding, the functions using event names or element names are not anymore cluttered with type parameters.
348
+
349
+
\section{Validation}
350
+
\label{sec-validation}
351
+
352
+
\subsection{Implementation in js-scala}
353
+
354
+
We implemented our pattern in js-scala~\cite{Kossakowski12_JsDESL}, a Scala library providing JavaScript code generators\footnote{Source code is available at\href{http://github.com/js-scala}{http://github.com/js-scala}}.
355
+
323
356
\subsection{Limitations}
324
357
325
358
We do not help with\jscode{getElementById}.
@@ -334,6 +367,8 @@ \section{Related Works}
334
367
\section{Conclusion}
335
368
\label{sec-conclusion}
336
369
370
+
We presented two ways to encode dynamically typed browser functions in mainstream statically typed languages like Java and Scala, using\emph{generics} or path-dependent types. Our encoding gives more type safety than existing solutions, while keeping the same expression power and modularity level as the native API.