Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The code generated from "let open Functor(Module) in ..." is buggy. #661

Closed
kxc-wraikny opened this issue Jul 18, 2023 · 6 comments · Fixed by #664
Closed

The code generated from "let open Functor(Module) in ..." is buggy. #661

kxc-wraikny opened this issue Jul 18, 2023 · 6 comments · Fixed by #664

Comments

@kxc-wraikny
Copy link

kxc-wraikny commented Jul 18, 2023

Summary

OCaml source code:

let test3 () =
  let open Samplelib.MonadOps(Promise) in
  return 2
  >>= fun x -> return (x + 40)
  >>= fun x -> return (to_be (expect x) 42)

The generated JavaScript:

function test3(param) {
  var open = Samplelib.MonadOps($$Promise$1);
  var $great$great$eq = open[1];
  var $$return = open[0];
  return Curry._2($great$great$eq, Curry._1($$return, 2), (function (x) {
                return Curry._2($great$great$eq, Curry._1($$return, x + 40 | 0), (function (x) {
                              return Curry._1($$return, (expect(x).toBe(42), undefined));
                            }));
              }));
}

Test result:

FAIL test/samplelib.test.js
  ✓ it loads (2 ms)
  ✓ test1 (1 ms)
  ✓ test2
  ✕ test3 (1 ms)

  ● test3

    TypeError: Cannot read properties of undefined (reading 'length')

      43 |   var $great$great$eq = open[1];
      44 |   var $$return = open[0];
    > 45 |   return Curry._2($great$great$eq, Curry._1($$return, 2), (function (x) {
         |                                          ^
      46 |                 return Curry._2($great$great$eq, Curry._1($$return, x + 40 | 0), (function (x) {
      47 |                               return Curry._1($$return, (expect(x).toBe(42), undefined));
      48 |                             }));

      at Object._1 (_output/node_modules/melange.runtime/curry.js:31:17)
      at Object._1 [as test3] (_output/samplelib_test.js:45:42)
      at Object.test3 (test/samplelib.test.js:17:29)

This is a reproduction repository:
https://github.com/kxc-wraikny/Melange_bug

Details

The functor is defined as

module MonadOps(M : sig
  type _ t
  val return : 'x -> 'x t
  val bind : 'x t -> ('x -> 'y t) -> 'y t
end) = struct
  let return = M.return
  let (>>=) = M.bind
end

https://github.com/kxc-wraikny/Melange_bug/blob/7f3aa164d5b0553424fe54638878717249855eb9/samplelib.ml#L1-L8

and this is the test code and the problem occurs at test3

module Test = struct
  let test1 () =
    let open Promise in
    return 2
    |> flip bind (fun x -> return (x + 40))
    |> flip bind @@ fun x -> return (to_be (expect x) 42)
    
  let test2 () =
    let module M = Samplelib.MonadOps(Promise) in
    let open M in
    return 2
    >>= fun x -> return (x + 40)
    >>= fun x -> return (to_be (expect x) 42)

  let test3 () =
    let open Samplelib.MonadOps(Promise) in
    return 2
    >>= fun x -> return (x + 40)
    >>= fun x -> return (to_be (expect x) 42)
end

https://github.com/kxc-wraikny/Melange_bug/blob/7f3aa164d5b0553424fe54638878717249855eb9/samplelib_test.ml#L19-L38

The generated code:

function test1(param) {
  var m = Promise.resolve(2);
  var m$1 = m.then(function (x) {
        return Promise.resolve(x + 40 | 0);
      });
  return m$1.then(function (x) {
              return Promise.resolve((expect(x).toBe(42), undefined));
            });
}

function test2(param) {
  var M = Samplelib.MonadOps($$Promise$1);
  return Curry._2(M.$great$great$eq, Curry._1(M.$$return, 2), (function (x) {
                return Curry._2(M.$great$great$eq, Curry._1(M.$$return, x + 40 | 0), (function (x) {
                              return Curry._1(M.$$return, (expect(x).toBe(42), undefined));
                            }));
              }));
}

function test3(param) {
  var open = Samplelib.MonadOps($$Promise$1);
  var $great$great$eq = open[1];
  var $$return = open[0];
  return Curry._2($great$great$eq, Curry._1($$return, 2), (function (x) {
                return Curry._2($great$great$eq, Curry._1($$return, x + 40 | 0), (function (x) {
                              return Curry._1($$return, (expect(x).toBe(42), undefined));
                            }));
              }));
}

https://github.com/kxc-wraikny/Melange_bug/blob/7f3aa164d5b0553424fe54638878717249855eb9/_output/samplelib_test.js#L22-L50

@anmonteiro
Copy link
Member

Where's Jest coming from?

@kxc-wraikny
Copy link
Author

It is added to package.json and called in the ml file using external, but I think that is not important because the generated js code itself is problematic.

open struct
  module Jest = struct
    type t
    external expect : 'a -> t = "expect" [@@bs.val]
    external to_be : t -> 'a -> unit = "toBe" [@@bs.send]
  end
end

https://github.com/kxc-wraikny/Melange_bug/blob/7b988ec6daaea197df4a8eac9059b9385b023a20/samplelib_test.ml#L7-L13

@anmonteiro
Copy link
Member

I couldn't repro it after removing Jest and using your code. The error is a bit weird, since the function passed to Curry._1 should always have an arity.

@anmonteiro
Copy link
Member

OK I managed to repro this, thanks.

@anmonteiro
Copy link
Member

BTW a workaround is to give the resulting module a name like in test2

@anmonteiro
Copy link
Member

fixing this in #664 and melange-re/melange-compiler-libs#18

anmonteiro added a commit to anmonteiro/opam-repository that referenced this issue Sep 14, 2023
CHANGES:

- Build executables for bytecode-only platforms too
  ([melange-re/melange#596](melange-re/melange#596))
- Move the entire builtin PPX to `melange.ppx`. Preprocessing with
  `melange.ppx` will needed in most cases going forward, as it's responsible
  for processing `external` declarations, `@deriving` attributes and more,
  compared to the previous release where `melange.ppx` just processed AST
  extension nodes ([melange-re/melange#583](melange-re/melange#583))
- Remove old BuckleScript-style conditional compilation
  ([melange-re/melange#605](melange-re/melange#605))
- Don't emit JS import / require paths with `foo/./bar.js`
  ([melange-re/melange#598](melange-re/melange#598),
  [melange-re/melange#612](melange-re/melange#612))
- Wrap the melange runtime
  ([melange-re/melange#624](melange-re/melange#624),
  [melange-re/melange#637](melange-re/melange#637)). After this change,
  Melange exposes fewer toplevel modules. Melange runtime / stdlib modules are
  now wrapped under:
    - `Caml*` / `Curry` modules are part of the runtime and keep being exposed
      as before
    - `Js.*` contains all the modules previously accessible via `Js_*`, e.g.
      `Js_int` -> `Js.Int`
    - `Belt.*` wraps all the `Belt` modules; `Belt_List` etc. are not exposed
      anymore, but rather nested under `Belt`, e.g. `Belt.List`
    - `Node.*`: we now ship a `melange.node` library that includes the modules
      containing Node.js bindings. After this change, users will have to depend
      on `melange.node` explicitly in order to use the `Node.*` modules
    - `Dom.*`: we now ship a `melange.dom` library that includes the modules
      containing Node.js bindings. This library is included by default so the
      `Dom` module will always be available in Melange projects.
- Disable warning 61 (`unboxable-type-in-prim-decl`) for externals generated by
  Melange ([melange-re/melange#641](melange-re/melange#641),
  [melange-re/melange#643](melange-re/melange#643))
- Add `--rectypes` ([melange-re/melange#644](melange-re/melange#644)) to
  enable [recursive
  types](https://v2.ocaml.org/releases/5.0/htmlman/types.html#sss:typexpr-aliased-recursive)
- [melange.ppx]: Deprecate `bs.*` attributes in favor of `mel.*`
  ([melange-re/melange#566](melange-re/melange#566),
  [melange-re/melange#662](melange-re/melange#662),
  [melange-re/melange#663](melange-re/melange#663))
- [melange]: Fix field access code generation when `open`in inline functor
  applications ([melange-re/melange#661](melange-re/melange#661),
  [melange-re/melange#664](melange-re/melange#664))
- [melange]: Upgrade the OCaml typechecker version to 5.1
  ([melange-re/melange#668](melange-re/melange#668))
- [melange.ppx]: Deprecate `[@@mel.val]` and suggest its removal. This
  attribute is redundant and unnecessary
  ([melange-re/melange#675](melange-re/melange#675),
  [melange-re/melange#678](melange-re/melange#678))
- [melange]: remove old, unused CLI flags: `-bs-ns`, `-bs-cmi`, `-bs-cmj`,
  `-bs-no-builtin-ppx`, `-bs-super-errors`
  ([melange-re/melange#686](melange-re/melange#686)).
- [melange]: generate correct code for types with the `option` shape
  ([melange-re/melange#700](melange-re/melange#700)).
- [melange]: stop exporting `$$default` in the generated JavaScript when using
  ES6 default exports `let default = ..`
  ([melange-re/melange#708](melange-re/melange#708)).
- [melange]: allow exporting invalid OCaml identifiers in the resulting
  JavaScript with `@mel.as`
  ([melange-re/melange#714](melange-re/melange#714), fixes
  [melange-re/melange#713](melange-re/melange#713)).
- [melange]: Allow using `@mel.as` in external declarations without explicitly
  annotating `@mel.{string,int}`
  ([melange-re/melange#722](melange-re/melange#722), fixes
  [melange-re/melange#578](melange-re/melange#578)).
- [melange]: Allow using `@mel.unwrap` in external declarations with `@mel.obj`
  ([melange-re/melange#724](melange-re/melange#724), fixes
  [melange-re/melange#679](melange-re/melange#679)).
- [melange]: Support renaming fields in inline records / record extensions with
  `@mel.as` ([melange-re/melange#732](melange-re/melange#732), fixes
  [melange-re/melange#730](melange-re/melange#730)).
mseri pushed a commit to ocaml/opam-repository that referenced this issue Sep 16, 2023
CHANGES:

- Build executables for bytecode-only platforms too
  ([melange-re/melange#596](melange-re/melange#596))
- Move the entire builtin PPX to `melange.ppx`. Preprocessing with
  `melange.ppx` will needed in most cases going forward, as it's responsible
  for processing `external` declarations, `@deriving` attributes and more,
  compared to the previous release where `melange.ppx` just processed AST
  extension nodes ([melange-re/melange#583](melange-re/melange#583))
- Remove old BuckleScript-style conditional compilation
  ([melange-re/melange#605](melange-re/melange#605))
- Don't emit JS import / require paths with `foo/./bar.js`
  ([melange-re/melange#598](melange-re/melange#598),
  [melange-re/melange#612](melange-re/melange#612))
- Wrap the melange runtime
  ([melange-re/melange#624](melange-re/melange#624),
  [melange-re/melange#637](melange-re/melange#637)). After this change,
  Melange exposes fewer toplevel modules. Melange runtime / stdlib modules are
  now wrapped under:
    - `Caml*` / `Curry` modules are part of the runtime and keep being exposed
      as before
    - `Js.*` contains all the modules previously accessible via `Js_*`, e.g.
      `Js_int` -> `Js.Int`
    - `Belt.*` wraps all the `Belt` modules; `Belt_List` etc. are not exposed
      anymore, but rather nested under `Belt`, e.g. `Belt.List`
    - `Node.*`: we now ship a `melange.node` library that includes the modules
      containing Node.js bindings. After this change, users will have to depend
      on `melange.node` explicitly in order to use the `Node.*` modules
    - `Dom.*`: we now ship a `melange.dom` library that includes the modules
      containing Node.js bindings. This library is included by default so the
      `Dom` module will always be available in Melange projects.
- Disable warning 61 (`unboxable-type-in-prim-decl`) for externals generated by
  Melange ([melange-re/melange#641](melange-re/melange#641),
  [melange-re/melange#643](melange-re/melange#643))
- Add `--rectypes` ([melange-re/melange#644](melange-re/melange#644)) to
  enable [recursive
  types](https://v2.ocaml.org/releases/5.0/htmlman/types.html#sss:typexpr-aliased-recursive)
- [melange.ppx]: Deprecate `bs.*` attributes in favor of `mel.*`
  ([melange-re/melange#566](melange-re/melange#566),
  [melange-re/melange#662](melange-re/melange#662),
  [melange-re/melange#663](melange-re/melange#663))
- [melange]: Fix field access code generation when `open`in inline functor
  applications ([melange-re/melange#661](melange-re/melange#661),
  [melange-re/melange#664](melange-re/melange#664))
- [melange]: Upgrade the OCaml typechecker version to 5.1
  ([melange-re/melange#668](melange-re/melange#668))
- [melange.ppx]: Deprecate `[@@mel.val]` and suggest its removal. This
  attribute is redundant and unnecessary
  ([melange-re/melange#675](melange-re/melange#675),
  [melange-re/melange#678](melange-re/melange#678))
- [melange]: remove old, unused CLI flags: `-bs-ns`, `-bs-cmi`, `-bs-cmj`,
  `-bs-no-builtin-ppx`, `-bs-super-errors`
  ([melange-re/melange#686](melange-re/melange#686)).
- [melange]: generate correct code for types with the `option` shape
  ([melange-re/melange#700](melange-re/melange#700)).
- [melange]: stop exporting `$$default` in the generated JavaScript when using
  ES6 default exports `let default = ..`
  ([melange-re/melange#708](melange-re/melange#708)).
- [melange]: allow exporting invalid OCaml identifiers in the resulting
  JavaScript with `@mel.as`
  ([melange-re/melange#714](melange-re/melange#714), fixes
  [melange-re/melange#713](melange-re/melange#713)).
- [melange]: Allow using `@mel.as` in external declarations without explicitly
  annotating `@mel.{string,int}`
  ([melange-re/melange#722](melange-re/melange#722), fixes
  [melange-re/melange#578](melange-re/melange#578)).
- [melange]: Allow using `@mel.unwrap` in external declarations with `@mel.obj`
  ([melange-re/melange#724](melange-re/melange#724), fixes
  [melange-re/melange#679](melange-re/melange#679)).
- [melange]: Support renaming fields in inline records / record extensions with
  `@mel.as` ([melange-re/melange#732](melange-re/melange#732), fixes
  [melange-re/melange#730](melange-re/melange#730)).
nberth pushed a commit to nberth/opam-repository that referenced this issue Jun 18, 2024
CHANGES:

- Build executables for bytecode-only platforms too
  ([melange-re/melange#596](melange-re/melange#596))
- Move the entire builtin PPX to `melange.ppx`. Preprocessing with
  `melange.ppx` will needed in most cases going forward, as it's responsible
  for processing `external` declarations, `@deriving` attributes and more,
  compared to the previous release where `melange.ppx` just processed AST
  extension nodes ([melange-re/melange#583](melange-re/melange#583))
- Remove old BuckleScript-style conditional compilation
  ([melange-re/melange#605](melange-re/melange#605))
- Don't emit JS import / require paths with `foo/./bar.js`
  ([melange-re/melange#598](melange-re/melange#598),
  [melange-re/melange#612](melange-re/melange#612))
- Wrap the melange runtime
  ([melange-re/melange#624](melange-re/melange#624),
  [melange-re/melange#637](melange-re/melange#637)). After this change,
  Melange exposes fewer toplevel modules. Melange runtime / stdlib modules are
  now wrapped under:
    - `Caml*` / `Curry` modules are part of the runtime and keep being exposed
      as before
    - `Js.*` contains all the modules previously accessible via `Js_*`, e.g.
      `Js_int` -> `Js.Int`
    - `Belt.*` wraps all the `Belt` modules; `Belt_List` etc. are not exposed
      anymore, but rather nested under `Belt`, e.g. `Belt.List`
    - `Node.*`: we now ship a `melange.node` library that includes the modules
      containing Node.js bindings. After this change, users will have to depend
      on `melange.node` explicitly in order to use the `Node.*` modules
    - `Dom.*`: we now ship a `melange.dom` library that includes the modules
      containing Node.js bindings. This library is included by default so the
      `Dom` module will always be available in Melange projects.
- Disable warning 61 (`unboxable-type-in-prim-decl`) for externals generated by
  Melange ([melange-re/melange#641](melange-re/melange#641),
  [melange-re/melange#643](melange-re/melange#643))
- Add `--rectypes` ([melange-re/melange#644](melange-re/melange#644)) to
  enable [recursive
  types](https://v2.ocaml.org/releases/5.0/htmlman/types.html#sss:typexpr-aliased-recursive)
- [melange.ppx]: Deprecate `bs.*` attributes in favor of `mel.*`
  ([melange-re/melange#566](melange-re/melange#566),
  [melange-re/melange#662](melange-re/melange#662),
  [melange-re/melange#663](melange-re/melange#663))
- [melange]: Fix field access code generation when `open`in inline functor
  applications ([melange-re/melange#661](melange-re/melange#661),
  [melange-re/melange#664](melange-re/melange#664))
- [melange]: Upgrade the OCaml typechecker version to 5.1
  ([melange-re/melange#668](melange-re/melange#668))
- [melange.ppx]: Deprecate `[@@mel.val]` and suggest its removal. This
  attribute is redundant and unnecessary
  ([melange-re/melange#675](melange-re/melange#675),
  [melange-re/melange#678](melange-re/melange#678))
- [melange]: remove old, unused CLI flags: `-bs-ns`, `-bs-cmi`, `-bs-cmj`,
  `-bs-no-builtin-ppx`, `-bs-super-errors`
  ([melange-re/melange#686](melange-re/melange#686)).
- [melange]: generate correct code for types with the `option` shape
  ([melange-re/melange#700](melange-re/melange#700)).
- [melange]: stop exporting `$$default` in the generated JavaScript when using
  ES6 default exports `let default = ..`
  ([melange-re/melange#708](melange-re/melange#708)).
- [melange]: allow exporting invalid OCaml identifiers in the resulting
  JavaScript with `@mel.as`
  ([melange-re/melange#714](melange-re/melange#714), fixes
  [melange-re/melange#713](melange-re/melange#713)).
- [melange]: Allow using `@mel.as` in external declarations without explicitly
  annotating `@mel.{string,int}`
  ([melange-re/melange#722](melange-re/melange#722), fixes
  [melange-re/melange#578](melange-re/melange#578)).
- [melange]: Allow using `@mel.unwrap` in external declarations with `@mel.obj`
  ([melange-re/melange#724](melange-re/melange#724), fixes
  [melange-re/melange#679](melange-re/melange#679)).
- [melange]: Support renaming fields in inline records / record extensions with
  `@mel.as` ([melange-re/melange#732](melange-re/melange#732), fixes
  [melange-re/melange#730](melange-re/melange#730)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants