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

Submodule functor call is curried, but the target function is uncurried #7245

Open
3 of 5 tasks
dsiu opened this issue Jan 12, 2025 · 7 comments
Open
3 of 5 tasks

Submodule functor call is curried, but the target function is uncurried #7245

dsiu opened this issue Jan 12, 2025 · 7 comments

Comments

@dsiu
Copy link

dsiu commented Jan 12, 2025

I am porting bs-bastet to modern ReScript (v12+) (WIP at https://github.com/dsiu/rescript-bastet/tree/rescript-v12)

In [email protected] (alpha.4 and later), the generated js for nested module functor invocation is incorrect.

The line:

let EQ = M.Eq(IntEq)(ArbitraryInt);

is assuming M.eq is curried while M.eq is defined as:

function Eq(E, A) {
  let suite = name => "suite: " + name;
  return {
    suite: suite
  };
}

let M = {
  Eq: Eq
};

which clearly isn't curried.

The error is:

{
  "message":"M.Eq(IntEq) is not a function. (In 'M.Eq(IntEq)(ArbitraryInt)', 'M.Eq(IntEq)' is an instance of Object)",
  "line":54,
  "column":21,
  "stack":"eval code@\neval@[native code]\n@about:srcdoc:39:20"
}

In rescript@11, the error line was compiled to :

let EQ = Curry._2(Eq, IntEq, ArbitraryInt);

which runs correctly.

Playground link for test code

Test Code

module type TEST = {
  type test
}

module type ARBITRARY = {
  type t
  type arbitrary<'a>
}

module type QUICKCHECK = {
  type t
  type arbitrary<'a>
}

module Interface = {
  module type EQ = {
    type t

    let eq: (t, t) => bool
  }
}

module Make = (T: TEST, Q: QUICKCHECK with type t = T.test) => {
  type t
  type arbitrary<'a>

  module Eq = (
    E: Interface.EQ,
    A: ARBITRARY with type t := E.t and type arbitrary<'a> := Q.arbitrary<'a>,
  ) => {
    let suite = name => "suite: " ++ name
  }
}

module IntWithTest = (
  T: TEST,
  Q: QUICKCHECK with type t = T.test,
  A: ARBITRARY with type t := int and type arbitrary<'a> := Q.arbitrary<'a>,
) => {
  module IntEq: Interface.EQ = {
    type t = int

    let eq = (a, b) => Int.equal(a, b)
  }
  module M = Make(T, Q)
  module EQ = M.Eq(IntEq, A)
}

module ArbitraryInt: ARBITRARY with type t = int and type arbitrary<'a> = array<'a> = {
  type t = int
  type arbitrary<'a> = array<'a>
}

module JsQuickCheck = {
  type t = int
  type arbitrary<'a> = array<'a>
}

module MochaTest = {
  type test = int
}

module TestInt = IntWithTest(MochaTest, JsQuickCheck, ArbitraryInt)

TestInt.EQ.suite->Console.log
// Generated by ReScript, PLEASE EDIT WITH CARE


let Interface = {};

function Make(T, Q) {
  let Eq = (E, A) => {
    let suite = name => "suite: " + name;
    return {
      suite: suite
    };
  };
  return {
    Eq: Eq
  };
}

function IntWithTest(T, Q, A) {
  let eq = (a, b) => a === b;
  let IntEq = {
    eq: eq
  };
  let Eq = (E, A) => {
    let suite = name => "suite: " + name;
    return {
      suite: suite
    };
  };
  let M = {
    Eq: Eq
  };
  let EQ = M.Eq(IntEq)(A);
  return {
    IntEq: IntEq,
    M: M,
    EQ: EQ
  };
}

let ArbitraryInt = {};

let JsQuickCheck = {};

let MochaTest = {};

function eq(a, b) {
  return a === b;
}

let IntEq = {
  eq: eq
};

function Eq(E, A) {
  let suite = name => "suite: " + name;
  return {
    suite: suite
  };
}

let M = {
  Eq: Eq
};

let EQ = M.Eq(IntEq)(ArbitraryInt);

let TestInt = {
  IntEq: IntEq,
  M: M,
  EQ: EQ
};

console.log(EQ.suite);

export {
  Interface,
  Make,
  IntWithTest,
  ArbitraryInt,
  JsQuickCheck,
  MochaTest,
  TestInt,
}
/* EQ Not a pure module */

Thank you for filing! Check list:

  • Is it a bug? Usage questions should often be asked in the forum instead.
  • Concise, focused, friendly issue title & description.
  • A minimal, reproducible example.
  • OS and browser versions, if relevant.
  • Is it already fixed in master? Instructions
@dsiu dsiu changed the title Sub module functor invocation is generated incorrectly with curried sematic while the target function definition is generated as uncurried Submodule functor call is curried, but the target function is uncurried Jan 12, 2025
dsiu added a commit to dsiu/rescript-bastet that referenced this issue Jan 12, 2025
dsiu added a commit to dsiu/rescript-bastet that referenced this issue Jan 12, 2025
dsiu added a commit to dsiu/rescript-bastet that referenced this issue Jan 12, 2025
@cristianoc
Copy link
Collaborator

Here's a small example:

module Make = (T: {}, Q: {}) => {
  module Eq = (E: {}, A: {}) => {}
}

module M = Make({}, {})

module EQ = M.Eq({}, {})

produces

function Make(T, Q) {
  let Eq = (E, A) => ({});
  return {
    Eq: Eq
  };
}

function Eq(E, A) {
  return {};
}

let M = {
  Eq: Eq
};

let EQ = M.Eq({})({});

Seems like a certain level of nesting is required to observe this.

@dsiu
Copy link
Author

dsiu commented Feb 2, 2025

@cristianoc : is this issue hard to fix? it was working on v12-alpha.3 and before so the bug was introduced recently. thanks a lot.

@cristianoc
Copy link
Collaborator

Perhaps one could always emit functors as functions taking one argument at a time, unless there are some issue that could come with that.

@cristianoc
Copy link
Collaborator

Something like this: #7273
If it works and it is safe, one can explore further.

@cristianoc
Copy link
Collaborator

@dsiu could you try to install the package from CI and see if it works for you?

@dsiu
Copy link
Author

dsiu commented Feb 5, 2025

@cristianoc sure thing. Will revert back once I tested it. Thanks!

@dsiu
Copy link
Author

dsiu commented Feb 6, 2025

@cristianoc, I'm happy to report that #7273 fixes the problem. I ran it against my larger code base, and it only changes the module functor invocations, not any other code. I think it is good to go.

Thanks again.

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

No branches or pull requests

2 participants