leostera / caramel

:candy: a functional language for building type-safe, scalable, and maintainable applications
https://caramel.run
Apache License 2.0
1.05k stars 25 forks source link

Should && and || translate to short-circuiting andalso and orelse? #57

Open michallepicki opened 3 years ago

michallepicki commented 3 years ago

To Reproduce

  1. Create a file main.ml with
    
    let print_int number = Io.format "~0tp~n" [ number ]

let print_and_return_true number = print_int number ; true

let main _ = print_int 0 ; false && print_and_return_true 1 ; true || print_and_return_true 2 ; print_int 3


2. Run command `caramel compile main.ml && escript main.erl`
3. See result:

0 1 2 3


**Expected behavior**
If the operators should be short-circuiting, the correct result would be:

0 3


just like when running this ocaml file:
```ocaml
(* let print_int number = Io.format "~0tp~n" [ number ] *)

let print_and_return_true number =
  print_int number ;
  true ;;

let main _ =
  print_int 0 ;
  false && print_and_return_true 1 ;
  true || print_and_return_true 2 ;
  print_int 3 ;;

main ()
$ ocaml main.ml
0
3

If the operators should be short-circuiting, I think the correct Erlang to generate here might be:

-spec main(_) -> ok.
main(_) ->
  print_int(0),
  false andalso print_and_return_true(1),
  true orelse print_and_return_true(2),
  print_int(3).

Environment (please complete the following information):

michallepicki commented 3 years ago

I am not super sure about the short-circuit operators semantics in OCaml, I found this issue that confuses me more than helps: https://stackoverflow.com/questions/23833221/order-of-evaluation-for-short-circuit-operators-and-let-in-ocaml

But in general, OCaml stdlib documentation says they should be short-circuiting, so I think they should translate to andalso and orelse as noted above.

michallepicki commented 3 years ago

I looked into fixing this and it seems that the way the code generation currently works, it would be easiest to have additional functions in caramel_runtime because andalso and orelse are not functions exported from the erlang module. I am honestly surprised the other operators are. I think there's no guarantee that it will stay this way as they are not documented there.

Should I send a PR that adds those functions to the runtime module? I think ideally all operators would get inlined without additional function calls but I understand this would complicate supporting passing them as arguments to higher order functions.

edit: but then, if these would work through a function call, they would no longer be short-circuiting anymore I think (?)

michallepicki commented 3 years ago

edit: comment moved to https://github.com/AbstractMachinesLab/caramel/issues/70