leostera / caramel

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

Getting the README example running #19

Closed seanhinde closed 4 years ago

seanhinde commented 4 years ago

Readme example I'm trying to get the server on the README page running. Two things so far:

  1. Process.make - I'm thinking this should be Process.spawn now?
  2. I can't figure out how to get erlang versions of the stdlib runtime modules

To Reproduce Steps to reproduce the behavior:

  1. Create a new rebar3 app
  2. Add the caramel README code to src/holder_annotated.ml
  3. caramel compile holder_annotated.ml
  4. rebar3 shell
  5. holder_annotated:start().
  6. The process erlang module is (obviously) not found

Expected behavior The server should run

What I've tried I tried the obvious of generating .erl files from the stdlib .ml by running caramel compile *.ml in stdlib/{beam|ocaml} but get circular dependency errors:

~/local/bin/stdlib/beam$ caramelc.exe compile *.ml
File "_none_", line 1:
Error: cycle in dependencies. End of list is not sorted.
beam.ml: Beam.ml Stdlib_binary.ml Stdlib_calendar.ml Stdlib_erlang.ml Stdlib_ets.ml Stdlib_gen_server.ml Stdlib_gen_statem.ml Stdlib_io.ml Stdlib_lists.ml Stdlib_maps.ml Stdlib_process.ml Stdlib_supervisor.ml Stdlib_sys.ml Stdlib_timer.ml

This is with the tagged release 0.0.6 that includes the stdlib ml files in the bistro.

Any pointers welcome. I'll have a go at a rebar3 plugin if I can figure out a working manual workflow.

Cool project !

leostera commented 4 years ago

Thanks for bringing this up! Excellent point. I'll have to add a flag so there could be multiple compilation targets at one go. Then Caramel can ship its .erl stdlib as well so you can just point to it with erlc's -pa path flags.

Working out a rebar3 plugin would be fantastic šŸ™ŒšŸ¼ I'm thinking the plugin could just download the appropriate binary and then you have full control over the paths of the stdlib as well.

seanhinde commented 4 years ago

Sounds great!

leostera commented 4 years ago

Oops! Apparently the word "fix" closes the issue šŸ˜…

seanhinde commented 4 years ago

Seeing some compile errors in the generated stdlib_process.erl:

===> Compiling src/stdlib_process.erl failed src/stdlib_process.erl:17: type variable 'A' is only used once (is unbound) src/stdlib_process.erl:19: function '__caramel_recv'/0 undefined src/stdlib_process.erl:25: type variable 'A' is only used once (is unbound) src/stdlib_process.erl:29: function recv/0 undefined src/stdlib_process.erl:36: ambiguous call of overridden pre R14 auto-imported BIF spawn/1

I'm on OTP 23

leostera commented 4 years ago

I managed to clear out all those errors you mentioned, except the recv/0 function being undefined. This is a blocking issue I need to fix first: https://github.com/AbstractMachinesLab/caramel/issues/10

seanhinde commented 4 years ago

Do let me know if you have stuff you want to get sorted before dealing with this. I can come back :)

leostera commented 4 years ago

I think #10 is a good next thing to get done. After that this issue might be resolved.

I'll make a new issue for allowing compiler annotations that can be turned into module attributes, to allow for -compile({no_auto_import,[spawn/1]}) to be set. This seems useful as well.

seanhinde commented 4 years ago

Some more progress today. I have the README example running. Hello Joe! (RIP)

A few things needed:

  1. Copied process.erl and beam_io.erl into my project and manually fixed up the errors and warnings in both. The other .erl files (maps.erl etc) fight with the OTP modules with the same names - I think they need to go back to being prefixed even if it means lots of name mangling / unmangling.

  2. Provide a stdlib-path. The default stdlib-path for caramelc isn't right any more. I had to use --stdlib-path /home/sean/.opam/default/lib/caramel/stdlib

  3. Added a .merlin file to my erlang project to get ocaml editor support. Io wasn't found - I had to change the code to Beam_io:format for merlin to find the definition.

Super nice :)

leostera commented 4 years ago

Regarding the --stdlib-path flag, keep an eye on this build -- it fixes the paths so you can use make install and it'll find the stdlib there too. I tried it locally and it seemed to work on Ubuntu 20.04.

leostera commented 4 years ago

Regarding the additional .erl files -- currently the only one that is included in the release tarball is process.erl, since the rest should be empty or point to types that should be mapped to OTP types.

seanhinde commented 4 years ago

Closer and closer. stdlib-path default now works for me after make install from latest head.

Still a few smaller things I'm sure you already have on your list:

  1. The generated process.erl still has type variables the erlang compiler doesn't like
  2. I kind of feel the need for an Io.ml so merlin can work. You have beam_io.ml - I guess this is a workaround for some other issue?

I will have a go at writing some more programs and exploring the possibilities

leostera commented 4 years ago

@seanhinde yup! getting there šŸš€

The latest head should produce the following process.erl, which for me compiles without warnings on Erlang 23. Could you try on a clean build and see if you get the same file?

% Source code generated with Caramel.
-module(process).
-export_type([after_time/0]).
-export_type([recv/1]).

-export([contramap/2]).
-export([make/1]).
-export([recv/1]).
-export([send/2]).

-type after_time() :: infinity
                    | {bounded, integer()}
                    .

-type recv(M) :: fun((after_time()) -> option:t(M)).

-spec recv(after_time()) -> option:t(any()).
recv(Timeout) ->
  F = fun (T) -> receive X -> {some, X} after T -> none end end,
  case Timeout of
    infinity -> F(infinity);
    {bounded, T} -> F(T)
  end.

-spec make(fun((erlang:pid(), recv(_m)) -> _a)) -> erlang:pid().
make(F) -> erlang:spawn(fun
  () ->
  Pid = erlang:self(),
  F(Pid, fun recv/1)
end).

-spec send(erlang:pid(), _m) -> ok.
send(Proc, Msg) -> erlang:send(Proc, Msg).

-spec contramap(fun((_b) -> _a), erlang:pid()) -> erlang:pid().
contramap(F, Pid) -> make(fun
  (_self, Recv) ->
  case Recv(infinity) of
    {some, A} -> send(Pid, F(A));
    none -> ok
  end
end).

For 2) could you try using the full path? Beam.Io.format? The Beam module is opened by default, but otherwise should still be available.

seanhinde commented 4 years ago

process.erl looking good. no more warnings :)

Beam.Io.format fixes merlin, but compiles to:

beam__io:format(<<"current_state: ~p\n">>, [State | []])

beam__io is not an erlang module in the tree. beam_io.erl exists in your generated files but uses a non existing '__unsafe_fmt'(S, X).

As an experiment I renamed beam_io.ml to io.ml and changed the dune files and beam.ml to match. Everything now seems to work.. but I'm not experienced with ocaml so don't know what else I might have broken.

leostera commented 4 years ago

Sweet! Yeah there'll be some .erl files that are not included in the actual release (beam_io.erl being one of them), since they are just compilation garbage.

Could you try making a local alias?

module Io = Beam.Io

Or with an explicit open?

open Beam
leostera commented 4 years ago

@seanhinde I figured what was breaking with the module name and now its called just io.ml :) so Merlin should work just fine now.

Quite a ride! šŸŽ¢ -- thanks for all the feedback btw, it helped me get the release in order and I'm a lot happier with it now. We covered:

Let me know if you give that rebar3 plugin a shot :) if rebar3 supports plugins that are nested within a git repo, i'd be happy to include it in here too.

Closing this for now, feel free to open other issues if you find anything šŸ™ŒšŸ¼

seanhinde commented 4 years ago

Thanks so much. Super cool. I'll take a look at a rebar3 plugin - no promises when though - I will also spend some time using the system.