-
Notifications
You must be signed in to change notification settings - Fork 47
Description
Bridges are to MLFE as ports are to Elm, without the send/receive and subscription semantics.
This is motivated by questions from @imetallica, discussion and feedback from @omarkj, and naming concerns from @lpil.
Example:
bridge append_ints = :erlang :"++" [list int, list int] list_int
Given the above in a module, the compiler will synthesize the function append_ints
, typed to take to integer lists and return one that is a combination of both:
{t_arrow, [{t_list, t_int}, {t_list, t_int}], {t_list, t_int}}
The typer will trust that the author has considered the types involved and will expose this function for type checking. The code generator will create this function in the output Core Erlang AST and programmatically create the necessary checks for the return value. If we follow what Elm has done, this will create some substantial overhead on any recursive type like lists, maps, and recursive ADTs as each element must be checked before returning the result to MLFE code. A more problematic example:
type maybe_io_device = Ok pid unit | Error atom
bridge open_file = :file :open [string, list atom] io_device
If you refer to the erldocs for file:open/2, you'll notice the types I've given above to the bridge are incomplete, for example I'm not accounting for the fd()
type which in the given docs doesn't appear to devolve to a pid. A larger issue is that currently the compiler would render the maybe_io_device
ADT as either {'Ok', Pid}
or {'Error', ErrorAtom}
in any pattern match checking the validity of the return. This is relatively trivial to change and may make sense for simpler handling of common Erlang patterns directly as ADTs with no intermediary translation layer at all.
More specifically, given the changes to how ADTs are rendered, the code above would be synthesized to the following in the code generator:
open_file(Filename, Modes) ->
case file:open(Filename, Modes) of
{ok, IO}=Ok when is_pid(IO) -> Ok;
{error, Reason}=Err when is_atom(Reason) -> Err
end.
This has rather large safety implications:
- do we let this explode on the Erlang side unchecked, somewhat similarly to Elm?
- do we generate code that is already wrapped in a try/catch to account for errors, utilizing some sort of built-in type like
type try 'x = Success 'x | Error erlang_exception
? I use this type in Scala but does this remove Erlang-ness from the language? - should we have a default safe mode as in the previous point and a keyword to remove the try/catch, e.g.
unsafe bridge open_file = ...
that doesn't wrap the result? - should bridges always be unsafe but any that occur without a surrounding try/catch raise a compiler warning?
I'm leaning towards point 3 at the moment but curious about other opinions and would like to know if I've missed anything (beyond the complexity checking recursive structures entails).