leostera / caramel

:candy: a functional language for building type-safe, scalable, and maintainable applications

Home Page:https://caramel.run

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unclear behavior with externals

leostera opened this issue · comments

When writing FFIs, regardless of the structure of the code on the Caramel side, we want all of it to go away.

I'd like FFIs to be cheap, so I can throw an external pretty much anywhere and be able to call that function.

Here's some examples that are inconsistent right now. The first external is the only one that behaves correctly I think:

(* file: todo.ml *)
external uniform : unit -> float  = ""
external uniform_with_ceiling : int -> int  = "uniform"

module Rand = struct
  external uniform : unit -> float  = ""
  external uniform_with_ceiling : int -> int  = "uniform"
end

external rand_uniform : unit -> float  = "rand:uniform"
external rand_uniform_with_ceiling : int -> int  = "rand:uniform"

let run () =
  let a = uniform () in
  let b = uniform_with_ceiling 2 in
  let c = Rand.uniform () in
  let d = Rand.uniform_with_ceiling 2 in
  let e = rand_uniform () in
  let f = rand_uniform_with_ceiling 2 in
  0

Generates the following Erlang:

-spec run() -> integer().                                                        
run() ->                                                                         
  A = uniform(),                                                                 
  B = uniform_with_ceiling(2),                                                   
  C = todo__rand:uniform(),                                                      
  D = todo__rand:uniform(2),                                                     
  E = rand_uniform(),                                                            
  F = rand_uniform_with_ceiling(2),                                              
  0.

Whereas what I'd expect is:

-spec run() -> integer().
run() ->
  A = uniform(),
  B = uniform(2),
  C = uniform(),
  D = uniform(2),
  E = rand:uniform(),
  F = rand:uniform(2),
  0.

NOTE: I'd expect this to work only because we have no structure within the symbol name string.

I think we need to do some refactoring in the compiler to make it easier to understand what's happening with the FFIs, so we can guarantee a more uniform behavior.

Current workaround is to make a new .ml file with the name of the module you want, and make it include only externals.

(* file: rand.ml *)
external uniform : unit -> float  = ""
external uniform_with_ceiling : int -> int  = "uniform"

Generates no .erl file ✅ but when called as Rand.uniform () translates to rand:uniform().

Example:

let run () = Rand.uniform ()
run() -> rand:uniform().