USUALLY when we say 'gradual typing', we mean gradually adding type annotations to a dynamically typed codebase like JavaScript or Python, and then running it through a typechecker like Flow/TypeScript/mypy. However, an equivalent approach is to leave the existing codebase as it is, write typed bindings to it using the FFI capabilities of a statically-typed language, and use those typed bindings to interact with it.
This of course implies that we have no intention of retiring the original codebase; we simply want to interoperate with it as safely as possible. This is an especially ideal scenario when we're dealing with a massively successful and active body of code like that of the JavaScript community.
In fact, TypeScript and mypy themselves adopt this approach--using typed bindings. Their communities maintain massive libraries of library bindings ready for consumption. In this post, I'm going to demonstrate how BuckleScript actually allows us to gradually introduce type-safe bindings to existing JavaScript APIs.
console.log
We'll start with a simple example. From using JavaScript before, we
will know that the built-in JavaScript Web API function
console.log
prints out an object that we give it on the
browser's JavaScript log:
console.log(obj);
So in BuckleScript terms, we want to bind to a global value
console.log
that takes a single value and
returns ... nothing. However, in BuckleScript (i.e., OCaml), all
functions have to return some value. What do we return when we
return nothing?
Well, since we're practising gradual typing, we won't worry about that right now, we'll just say that we return 'whatever':
external consoleLog : 'obj -> _ = "console.log" [@@bs.val]
According to the BuckleScript manual, the [@@bs.val]
attribute binds a given name to a given global value. In this case, the
value is the console.log
function. The
'obj
type means 'any type, let's call it
"obj" ', and the _
'wildcard' type means 'I
don't care what the type is, let the typechecker infer it'.
If you bind and then call this function in the BuckleScript playground:
external consoleLog : 'obj -> _ = "console.log" [@@bs.val]
let () = consoleLog "Hello, World!"
You get the following JavaScript output:
// Generated by BUCKLESCRIPT VERSION 1.9.2, PLEASE EDIT WITH CARE 'use strict'; console.log("a"); /* Not a pure module */
So, a binding with almost no concrete type information (except that
it's a function of one argument) compiles down to JavaScript that just
works! Admittedly, console.log
is a simple
function which accepts pretty much any input. In the next installment
(this one is getting a little long) I'll cover another slightly more
complex function and show how we can gradually type it in
BuckleScript.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.