Parameter types for function parameter, labels in tuples?


#1

A) I’ve been learning Reason for the last two months, so I’m almost certainly just being dumb here. B) The context is PR #35 on reductive, in which I’m trying to add something akin to a lens (a la react-redux).

let lensedComponent =
    (
      make:  /* line 111 */
        (~state: 'lensed, ~dispatch: 'action => unit, array(ReasonReact.reactElement)) =>
        ReasonReact.component('a, 'b, 'c),
      get: 'state => 'lensed,
      ~state: 'state,
      ~dispatch: 'action => unit,
      children: array(ReasonReact.reactElement),
    )
    : ReasonReact.component('a, 'b, 'c) => {
  let state: 'lensed = get(state);
  let baseComponent = make(~state, ~dispatch, children);
  {
    ...baseComponent,
    ReasonReact.retainedProps: state,
    shouldUpdate:
      (
        {
          oldSelf: {retainedProps: oldState},
          newSelf: {retainedProps: newState},
        } as total,
      ) =>
      oldState === newState ?
        false : baseComponent.ReasonReact.shouldUpdate(total),
  };
};

The 4.0.7 compiler does not like the labels on the parameters in the type signature of the make argument.

File "/Users/nfiedler/projects/reductive/src/reductive.re", line 112, characters 9-24:
Error: Syntax error: Labels are not allowed inside a tuple not expected.

But taking them away means the ~state argument in the call to make is somehow missing, and the compiler complains.

  119 ┆   : ReasonReact.component('a, 'b, 'c) => {
  120 ┆ let state: 'lensed = get(state);
  121 ┆ let baseComponent = make(~state, ~dispatch, children);
  122 ┆ {
  123 ┆   ...baseComponent,

  The function applied to this argument has type
    ('action => unit, array(ReasonReact.reactElement)) =>
    ReasonReact.componentSpec('a, 'a, 'b, 'b, 'c)
This argument cannot be applied with label ~state

Thank you in advance for any suggestions you can offer.


#2

Problem with parens:

Try it:

let lensedComponent =
    (
      make:
        (
          ~state: 'lensed,
          ~dispatch: 'action => unit,
          array(ReasonReact.reactElement)
        ) =>
        ReasonReact.component('a, 'b, 'c),
      get: 'state => 'lensed,
      ~state: 'state,
      ~dispatch: 'action => unit,
      children: array(ReasonReact.reactElement),
    )
    : ReasonReact.component('a, 'b, 'c) => {
  let state: 'lensed = get(state);
  let baseComponent = make(~state, ~dispatch, children);
  {
    ...baseComponent,
    ReasonReact.retainedProps: state,
    shouldUpdate:
      (
        {
          oldSelf: {retainedProps: oldState},
          newSelf: {retainedProps: newState},
        } as total,
      ) =>
      oldState === newState ?
        false : baseComponent.ReasonReact.shouldUpdate(total),
  };
};

#3

Sorry, even that doesn’t work.
It is necessary to add parens around the type hint of make, but refmt (that deletes them.

Example that works:

let func =
    (make: (~state: string, ~dispatch: string, array(string)) => string) => "";

If we click on Refmt (reformat) button, then refmt removes the parens and the error occurs.


#4

I needed a bit more detail.

Version that works:

let func = (_make:((~state: string, ~dispatch: string, array(string)) => string)):string => "";

Version that refmt produces that breaks as soon as you edit it:

let func =
    (_make: (~state: string, ~dispatch: string, array(string)) => string)
    : string => "";

Error:

Line 2:14-27 File "", line 2, characters 13-27:
Error: Syntax error: Labels are not allowed inside a tuple not expected.

Less fragile version:

type make_type = (~state: string, ~dispatch: string, array(string)) => string;

let func = (_make: make_type):string => "";

or even

type make_type = (~state: string, ~dispatch: string, array(string)) => string;
let func: make_type => string = _make => "";

So based on that:

type makeType('lensed, 'action, 'a, 'b, 'c) =
  (
    ~state: 'lensed,
    ~dispatch: 'action => unit,
    array(ReasonReact.reactElement)
  ) =>
  ReasonReact.component('a, 'b, 'c);

let lensedComponent =
    (
      make: makeType('lensed, 'action, 'a, 'b, 'c),
      get: 'state => 'lensed,
      ~state: 'state,
      ~dispatch: 'action => unit,
      children: array(ReasonReact.reactElement),
    )
    : ReasonReact.component('a, 'b, 'c) => {
  let state: 'lensed = get(state);
  let baseComponent = make(~state, ~dispatch, children);
  {
    ...baseComponent,
    ReasonReact.retainedProps: state,
    shouldUpdate:
      (
        {
          oldSelf: {retainedProps: oldState},
          newSelf: {retainedProps: newState},
        } as total,
      ) =>
      oldState === newState ?
        false : baseComponent.ReasonReact.shouldUpdate(total),
  };
};

compiles.

type makeType('lensed, 'action, 'a, 'b, 'c) =
  (
    ~state: 'lensed,
    ~dispatch: 'action => unit,
    array(ReasonReact.reactElement)
  ) =>
  ReasonReact.component('a, 'b, 'c);

type lensedComponentType('state, 'lensed, 'action, 'a, 'b, 'c) =
  (
    makeType('lensed, 'action, 'a, 'b, 'c),
    'state => 'lensed,
    ~state: 'state,
    ~dispatch: 'action => unit,
    array(ReasonReact.reactElement)
  ) =>
  ReasonReact.component('a, 'b, 'c);

let lensedComponent: lensedComponentType('state, 'lensed, 'action, 'a, 'b, 'c) =
  (make, get, ~state, ~dispatch, children) => {
    let state: 'lensed = get(state);
    let baseComponent = make(~state, ~dispatch, children);
    {
      ...baseComponent,
      ReasonReact.retainedProps: state,
      shouldUpdate:
        (
          {
            oldSelf: {retainedProps: oldState},
            newSelf: {retainedProps: newState},
          } as total,
        ) =>
        oldState === newState ?
          false : baseComponent.ReasonReact.shouldUpdate(total),
    };
  };

#5

Thanks for the replies, you’re quite right about the parens issue. In parallel, Ricky Vetter filed a report in reason/issues/2271 and got a response that the issue is resolved in the latest versions. So that’s good news, but in the mean time I can’t run refmt.


#6

refmt won’t break your code if you pull out the make function’s type into a type declaration as I demonstrated above.

type makeType('lensed, 'action, 'a, 'b, 'c) =
  (
    ~state: 'lensed,
    ~dispatch: 'action => unit,
    array(ReasonReact.reactElement)
  ) =>
  ReasonReact.component('a, 'b, 'c);

let lensedComponent =
    (
      make: makeType('lensed, 'action, 'a, 'b, 'c),
      get: 'state => 'lensed,
      ~state: 'state,
      ~dispatch: 'action => unit,
      children: array(ReasonReact.reactElement),
    )
    : ReasonReact.component('a, 'b, 'c) => {
  /* ... */
};

#7

So sorry, you’re right. When I read your post via email, I didn’t see the bottom half. Your suggestion works perfectly, thank you.