tc39 / proposal-do-expressions

Proposal for `do` expressions
MIT License
1.11k stars 14 forks source link

do expression, generator function and for loop #37

Closed xialvjun closed 3 years ago

xialvjun commented 5 years ago
import React from "react";
import { render } from "react-dom";
import { Element, init_state, init_value, init_ref, genc } from "@xialvjun/react-element";

export const GeneratorComponent = genc(function*(props) {
  const [pagination] = yield <Element construct={init_state({ limit: 10, offset: 0 })} />;
  const main = (
    <div>
      <Pager limit_and_offset={pagination.state} onChange={limit_and_offset => pagination.set_state(limit_and_offset)} />
      {do {
        const [editing_item] = yield <Element construct={init_value(null)} />;
        <>
          {do {
            const [{ loading, error, data }] = yield <Query query={all_items} variables={pagination.state} />;
            <table>
              <thead>
                <tr>
                  <th>name</th>
                  <th>age</th>
                  <th>photo</th>
                  <th>operation</th>
                </tr>
              </thead>
              <tbody>
                {loading && (
                  <tr>
                    <td colSpan={999}>loading</td>
                  </tr>
                )}
                {error && (
                  <tr>
                    <td colSpan={999}>{`${error}`}</td>
                  </tr>
                )}
                {(data.all_items || []).map(it => (
                  <tr key={it.id}>
                    <td>{it.name}</td>
                    <td>{it.age}</td>
                    <td>
                      <img src={it.photo} />
                    </td>
                    <td>
                      <button onClick={_ => editing_item.set_value(it)}>edit</button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          }}
          {editing_item.value && (
            <Modal open>
              <form>
                <div>
                  <label>name</label>
                  <input type="text" value={e => editing_item.set_partial("name", e.target.value)} />
                </div>
                <div>
                  <label>age</label>
                  <input type="number" value={e => editing_item.set_partial("age", parseInt(e.target.value) || 18)} />
                </div>
                <div>
                  <label>photo</label>
                  {do {
                    const [{ ref }] = yield <Element construct={init_ref()} />;
                    <Modal ref={ref} trigger={editing_item.value.photo ? <img src={editing_item.value.photo} /> : <Icon name="avatar" />}>
                      <ImageSelector
                        on_select={img => {
                          editing_item.set_partial("photo", img.url);
                          ref.close_modal();
                        }}
                      />
                    </Modal>
                  }}
                </div>
                {do {
                  const [edit_item_api, edit_item_res] = yield <Mutation mutation={edit_item} />;
                  <button type="button" disabled={edit_item_res.loading} onClick={_ => edit_item_api({ variables: editing_item.value })}>
                    submit
                  </button>
                }}
              </form>
            </Modal>
          )}
          <h4>and for loop</h4>
          {do {
            let c = 0;
            for (let i = 0; i < 5; i++) {
              // if genc find what yield is not a react element, it will not modify the yield thing.
              c += yield i;
            }
            let a = yield 1;
            let b = yield 2;
            <div>
              <p>a + b = {a + b}</p>
              <p>the result of c is {c}</p>
            </div>
          }}
        </>
      }}
    </div>
  );

  const rest_to_show_do_isnot_totally_hoist = (
    <div>
      {do {
        let c = 0;
        for (let i = 0; i < 5; i++) {
          // if genc find what yield is not a react element, it will not modify the yield thing.
          c += yield i;
        }
        let a = yield 1;
        let b = yield 2;
        <div>
          <p>a + b = {a + b}</p>
          <p>the result of c is {c}</p>
        </div>
      }}
    </div>
  );

  return (
    <div>
      {main}
      {rest_to_show_do_isnot_totally_hoist}
    </div>
  );
});

render(<GeneratorComponent />, document.querySelector("#root"));

But it compiles wrong...

xialvjun commented 5 years ago

In above codes, there are many dos.

They should compile to:

import React from "react";
import { render } from "react-dom";
import { Element, init_state, init_value, init_ref, genc } from "@xialvjun/react-element";

export const GeneratorComponent = genc(function*(props) {
  const [pagination] = yield <Element construct={init_state({ limit: 10, offset: 0 })} />;
  let do_1;
  {
    const [editing_item] = yield <Element construct={init_value(null)} />;
    let do_2;
    {
      const [{ loading, error, data }] = yield <Query query={all_items} variables={pagination.state} />;
      do_2 = (
        <table>
          <thead>
            <tr>
              <th>name</th>
              <th>age</th>
              <th>photo</th>
              <th>operation</th>
            </tr>
          </thead>
          <tbody>
            {loading && (
              <tr>
                <td colSpan={999}>loading</td>
              </tr>
            )}
            {error && (
              <tr>
                <td colSpan={999}>{`${error}`}</td>
              </tr>
            )}
            {(data.all_items || []).map(it => (
              <tr key={it.id}>
                <td>{it.name}</td>
                <td>{it.age}</td>
                <td>
                  <img src={it.photo} />
                </td>
                <td>
                  <button onClick={_ => editing_item.set_value(it)}>edit</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      );
    }
    let do_3;
    {
      const [{ ref }] = yield <Element construct={init_ref()} />;
      do_3 = (
        <Modal ref={ref} trigger={editing_item.value.photo ? <img src={editing_item.value.photo} /> : <Icon name="avatar" />}>
          <ImageSelector
            on_select={img => {
              editing_item.set_partial("photo", img.url);
              ref.close_modal();
            }}
          />
        </Modal>
      );
    }
    let do_4;
    {
      const [edit_item_api, edit_item_res] = yield <Mutation mutation={edit_item} />;
      do_4 = (
        <button type="button" disabled={edit_item_res.loading} onClick={_ => edit_item_api({ variables: editing_item.value })}>
          submit
        </button>
      );
    }
    let do_5;
    {
      let c = 0;
      for (let i = 0; i < 5; i++) {
        // if genc find what yield is not a react element, it will not modify the yield thing.
        c += yield i;
      }
      let a = yield 1;
      let b = yield 2;
      do_5 = (
        <div>
          <p>a + b = {a + b}</p>
          <p>the result of c is {c}</p>
        </div>
      );
    }
    do_1 = (
      <>
        {do_2}
        {editing_item.value && (
          <Modal open>
            <form>
              <div>
                <label>name</label>
                <input type="text" value={e => editing_item.set_partial("name", e.target.value)} />
              </div>
              <div>
                <label>age</label>
                <input type="number" value={e => editing_item.set_partial("age", parseInt(e.target.value) || 18)} />
              </div>
              <div>
                <label>photo</label>
                {do_3}
              </div>
              {do_4}
            </form>
          </Modal>
        )}
        <h4>and for loop</h4>
        {do_5}
      </>
    );
  }
  const main = (
    <div>
      <Pager limit_and_offset={pagination.state} onChange={limit_and_offset => pagination.set_state(limit_and_offset)} />
      {do_1}
    </div>
  );

  let do_6;
  {
    let c = 0;
    for (let i = 0; i < 5; i++) {
      // if genc find what yield is not a react element, it will not modify the yield thing.
      c += yield i;
    }
    let a = yield 1;
    let b = yield 2;
    do_6 = (
      <div>
        <p>a + b = {a + b}</p>
        <p>the result of c is {c}</p>
      </div>
    );
  }
  const rest_to_show_do_isnot_totally_hoist = <div>{do_6}</div>;

  return (
    <div>
      {main}
      {rest_to_show_do_isnot_totally_hoist}
    </div>
  );
});

render(<GeneratorComponent />, document.querySelector("#root"));
bakkot commented 3 years ago

I'm really unclear on what this is asking or asking for. Since this is several years old, I'm going to close it, but feel free to reopen if you have a concrete suggestion or question.

xialvjun commented 3 years ago
function A() {
  let p = 'asdasd';
  return (
    <div>
      <p>{p}</p>
      {do {
        let b = 123;
        <button>{b}</button>
      }}
    </div>
  );
}

function B() {
  let p = 'asdasd';
  return m(
    'div',
    m('p', p),
    do {
      let b = 123;
      m('button', b)
    },
  );
}

function* C() {
  let p = yield 'asdasd';
  return m(
    'div',
    m('p', p),
    do {
      let b = yield 123;
      m('button', b)
    },
  );
}

In this code, A B is ok, but C is wrong. C compiles to:

function* C() {
  let p = yield 'asdasd';
  return m('div', m('p', p), function () {
    let b = yield 123;
    return m('button', b);
  }());
}

but I expect it to be:

function* C() {
  let p = yield 'asdasd';
  return m('div', m('p', p), yield* function* () {
    let b = yield 123;
    return m('button', b);
  }());
}
bakkot commented 3 years ago

C compiles to:

Not according to the spec. That sounds like a bug in whatever you're using to do compilation (babel?), rather than in this proposal.

xialvjun commented 3 years ago

So, in this spec, C do compiles to what I want it to be, it's just babel's bug ?

babel implements do expression wrong ?

bakkot commented 3 years ago

It sounds like it, yes. I see you've already opened an issue there.