caramel icon indicating copy to clipboard operation
caramel copied to clipboard

Partial function application compiles to wrong erlang

Open michallepicki opened this issue 5 years ago • 9 comments

Describe the bug A simple function that taking no arguments in Caramel, that only runs Io.format, compiles to invalid erlang that says it is taking two arguments, which erlang/OTP cannot compile.

To Reproduce

$ caramelc --version
0.0.14+git-27bc02d
$ cat sweetness.ml 
let text () = "Hello, world!"

let hello () = Io.format (text ())
$ caramelc compile sweetness.ml
Compiling sweetness.erl OK
$ cat sweetness.erl 
% Source code generated with Caramel.
-module(sweetness).

-export([hello/2]).
-export([text/0]).

-spec text() -> binary().
text() -> <<"Hello, world!">>.

-spec hello(ok, list(any())) -> ok.
hello() -> io:format(text()).


$ erl
Erlang/OTP 23 [erts-11.1.6] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [hipe]

Eshell V11.1.6  (abort with ^G)
1> c(sweetness).
sweetness.erl:4: function hello/2 undefined
sweetness.erl:10: spec for undefined function hello/2
sweetness.erl:11: Warning: function hello/0 is unused
error

Expected behaviour .erl gets created with

-export([hello/0]).

-spec hello() -> fun((list(any())) -> ok).

michallepicki avatar Jan 23 '21 11:01 michallepicki

I think it may be because I thought there is an Io.format function that takes one argument but there's only a two argument version? Still I think this should not be the result, I don't know where the ok in spec comes from and why spec args do not align with function args. I don't know much Ocaml but should that compile to something like

-export([hello/1]).
-spec hello(list(any())) -> ok.
hello(args) -> io:format(text(), args).

? Or

-export([hello/0]).
-spec hello() -> fun((list(any())) -> ok).
hello() -> fun
     args -> io:format(text(), args)
   end.

?

michallepicki avatar Jan 23 '21 12:01 michallepicki

Also tried on 0fe81e7, same result

michallepicki avatar Jan 23 '21 14:01 michallepicki

Thanks for filing this!

Since Caramel is an ML, we do partial application by default. When you called Io.format x what you get back is a function 'a list -> unit.

You're correct that this is a bug because the signature of hello/0 is incorrect.

The right signature should be hello() -> fun( (list(_)) -> ok ).

leostera avatar Jan 23 '21 14:01 leostera

-export([hello/1]).
-spec hello(list(any())) -> ok.
hello(Args) -> io:format(text(), Args).

This doesn't quite track because in the Caramel source you still have to call hello () args, and that unit is being translated as an ok. Empty tuples {} in Erlang just don't aren't a convention.

leostera avatar Jan 23 '21 14:01 leostera

-export([hello/0]).
-spec hello() -> fun((list(any())) -> ok).
hello() -> fun (Args) -> io:format(text(), Args) end.

This would be the Right Thing ™️, but is highly unidiomatic Erlang code.

leostera avatar Jan 23 '21 15:01 leostera

I look into this.

nicobao avatar Mar 13 '21 18:03 nicobao

-export([hello/0]).
-spec hello() -> fun((list(any())) -> ok).
hello() -> fun (Args) -> io:format(text(), Args) end.

This would be the Right Thing tm, but is highly unidiomatic Erlang code.

So I suppose I should implement the Right Thing, even if it is highly unidiomatic?

nicobao avatar Mar 14 '21 13:03 nicobao

I tried:

nicolas@localhost:~/test-caramel$ caramel --version
0.1.0+git-23d695b

test_hello.ml

let text () = "Hello World" in

let hello () = Io.format (text ()) in hello ()

then caramel compile test_hello.ml

which leads to no error, a test_hello.cmi file but no erl file.

If I try with

test_ocaml.ml

let text () = "Hello World" in

let hello () = print_endline (text ()) in hello ()

and I do ocaml test_ocaml.ml, it returns Hello World.

Is that normal that in operator is not accepted top-level?

nicobao avatar Mar 14 '21 14:03 nicobao

@nicobao yes, I think they're ignored but they probably should fail with an error (just like let rec within a let).

The automatic uncurrying can be done (as shown by BuckleScript), but that's a larger task than undoing the work I did for it. Let's start there, translating the functions as they are, and we can optimize later 🙌🏽

leostera avatar Mar 15 '21 06:03 leostera