JSX for Smalltalk

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

JSX for Smalltalk

Herby Vojčík
Hello!

Citing from name of Eric Elliott's article, JSX looks like abomination
to many at first sight, but fact is, it _is_ very handy to have syntax
for defining structural trees. I use it in react native myself and got
used to it pretty fast.

So I spent some time trying to come up with the alternative for
Smalltalk. Taking this from some example site and adding a few usages so
all things are covered:

     <div className="widget">
         <UI.Logo className="center" src="http://example.com/logo"/>
         <hr />
         <Label className="default-label"
name={this.state.name}>Sample</Label>
         <Input className="default-input" smart
onChange={this.handleChange} suggestions={["none", this.name]}/>
         {this.debugInfo}
     </div>

I iterated to this:

     <div className: 'widget' |
         <(UI Logo) className: 'center'; src: 'http://example.com/logo'>.
         <hr>.
         <(Label) className: 'default-label'; name: (self state name) |
'Sample'>.
         <(Input) className: 'default-input'; smart; onChange: [:ev |
self handleChange: ev]; suggestions: {'none'. self name}>.
         (self debugInfo)
     >

with the interesting 'tagged dynamic array' alternative worth mentioning:

     {<div> className: 'widget' |
         {<(UI Logo)> className: 'center'; src: 'http://example.com/logo'}.
         {<hr>},
         {<(Label)> className: 'default-label'; name: (self state name)
| 'Sample'}.
         {<(Input)> className: 'default-input'; smart; onChange: ([:ev |
self handleChange: ev]); suggestions: ({'none'. self name})}.
         self debugInfo
     }

What is could be transcribed into (JSX is syntactic sugar; this thing I
call stx but can mayve be called stag would be also transformed in to
plain calls) is either:

     stx
         nodeFor: #div
         attributes: #{className -> 'widget'}
         children: {
             stx
                 nodeFor: (UI Logo)
                 attributes: #{
                     className -> 'center'.
                     src -> 'http://example/logo' }.
             stx nodeFor: #hr.
             stx
                 nodeFor: (Label)
                 attributes: #{
                     className -> 'default-label'.
                     name -> (self state name) };
                 children: {'Sample'}.
             stx
                 nodeFor: (Input)
                 attributes: #{
                     className -> 'default-input'.
                     smart -> (stx defaultFor: #smart).
                     onChange -> [:ev | self handleChange: ev].
                     suggestions -> {'none'. self name} } }

which is descriptive and uses single-phase selector family of
nodeFor:[attributes:][children:] and Amber-specific #{...} to define
HashedDictionary, or:

     ((stx nodeFor: #div)
         at: #className put 'widget';
         add: ((six nodeFor: (UI Logo))
             at: #className put: 'center';
             at: #src put: 'http://example/logo';
             value);
         add: (stx nodeFor: #hr) value;
         add: ((stx nodeFor: (Label))
             at: #className put: 'default-label';
             at: #name put: (self state name);
             add: 'Sample';
             value);
         add: ((stx nodeFor: (Input))
             at: #className put: 'default-input';
             at: smart put: (stx defaultFor: #smart);
             at: #onChange put: [:ev | self handleChange: ev];
             at: #suggestions put: {'none'. self name};
             value);
         value)

which is two-phase builder-like using nodeFor: to get builder, at:put:
and add: to fill it and value to produce the result.

What could it be good for? Building structures in readable way is not
easy in Smalltalk. So lazy ppl don't build them if they can avoid them.
One example that comes to mind (except obvious binding to react etc.) is
that I can write test on parts of compiler and feed the fake AST tree
there built by this syntax.

Why is lowercase 'stx' used for building? Because it makes for easy
replacement. There can be one global 'stx' defined (or not). In Amber,
one can use, say, `imports: { stx -> 'amber-stx-react' }` in a package
and defined package-local 'stx' to be the react bridge. Other packages
can use their own 'stx'. Last but not least, I can declare | stx | and
put `stx := MYStxBuilder new.` directly in a method.

Maybe using `self stx` is more smalltalkish?

What do you think?

Herby

P.S.: I can debate the alternatives that appeared during finding out the
syntax in different thread if someone is interested.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: JSX for Smalltalk

philippeback
Looks nice but I am out of my depth with React I am afraid (just read the doc, did a hello work example or two).

I did Angular and indeed it is not that friendly to Amber, so, yeah, there I stand.

Phil

On Wed, Sep 21, 2016 at 2:17 PM, Herby Vojčík <[hidden email]> wrote:
Hello!

Citing from name of Eric Elliott's article, JSX looks like abomination to many at first sight, but fact is, it _is_ very handy to have syntax for defining structural trees. I use it in react native myself and got used to it pretty fast.

So I spent some time trying to come up with the alternative for Smalltalk. Taking this from some example site and adding a few usages so all things are covered:

    <div className="widget">
        <UI.Logo className="center" src="http://example.com/logo"/>
        <hr />
        <Label className="default-label" name={this.state.name}>Sample</Label>
        <Input className="default-input" smart onChange={this.handleChange} suggestions={["none", this.name]}/>
        {this.debugInfo}
    </div>

I iterated to this:

    <div className: 'widget' |
        <(UI Logo) className: 'center'; src: 'http://example.com/logo'>.
        <hr>.
        <(Label) className: 'default-label'; name: (self state name) | 'Sample'>.
        <(Input) className: 'default-input'; smart; onChange: [:ev | self handleChange: ev]; suggestions: {'none'. self name}>.
        (self debugInfo)
    >

with the interesting 'tagged dynamic array' alternative worth mentioning:

    {<div> className: 'widget' |
        {<(UI Logo)> className: 'center'; src: 'http://example.com/logo'}.
        {<hr>},
        {<(Label)> className: 'default-label'; name: (self state name) | 'Sample'}.
        {<(Input)> className: 'default-input'; smart; onChange: ([:ev | self handleChange: ev]); suggestions: ({'none'. self name})}.
        self debugInfo
    }

What is could be transcribed into (JSX is syntactic sugar; this thing I call stx but can mayve be called stag would be also transformed in to plain calls) is either:

    stx
        nodeFor: #div
        attributes: #{className -> 'widget'}
        children: {
            stx
                nodeFor: (UI Logo)
                attributes: #{
                    className -> 'center'.
                    src -> 'http://example/logo' }.
            stx nodeFor: #hr.
            stx
                nodeFor: (Label)
                attributes: #{
                    className -> 'default-label'.
                    name -> (self state name) };
                children: {'Sample'}.
            stx
                nodeFor: (Input)
                attributes: #{
                    className -> 'default-input'.
                    smart -> (stx defaultFor: #smart).
                    onChange -> [:ev | self handleChange: ev].
                    suggestions -> {'none'. self name} } }

which is descriptive and uses single-phase selector family of nodeFor:[attributes:][children:] and Amber-specific #{...} to define HashedDictionary, or:

    ((stx nodeFor: #div)
        at: #className put 'widget';
        add: ((six nodeFor: (UI Logo))
            at: #className put: 'center';
            at: #src put: 'http://example/logo';
            value);
        add: (stx nodeFor: #hr) value;
        add: ((stx nodeFor: (Label))
            at: #className put: 'default-label';
            at: #name put: (self state name);
            add: 'Sample';
            value);
        add: ((stx nodeFor: (Input))
            at: #className put: 'default-input';
            at: smart put: (stx defaultFor: #smart);
            at: #onChange put: [:ev | self handleChange: ev];
            at: #suggestions put: {'none'. self name};
            value);
        value)

which is two-phase builder-like using nodeFor: to get builder, at:put: and add: to fill it and value to produce the result.

What could it be good for? Building structures in readable way is not easy in Smalltalk. So lazy ppl don't build them if they can avoid them. One example that comes to mind (except obvious binding to react etc.) is that I can write test on parts of compiler and feed the fake AST tree there built by this syntax.

Why is lowercase 'stx' used for building? Because it makes for easy replacement. There can be one global 'stx' defined (or not). In Amber, one can use, say, `imports: { stx -> 'amber-stx-react' }` in a package and defined package-local 'stx' to be the react bridge. Other packages can use their own 'stx'. Last but not least, I can declare | stx | and put `stx := MYStxBuilder new.` directly in a method.

Maybe using `self stx` is more smalltalkish?

What do you think?

Herby

P.S.: I can debate the alternatives that appeared during finding out the syntax in different thread if someone is interested.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.