-
Notifications
You must be signed in to change notification settings - Fork 27
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
Hello world example #48
base: master
Are you sure you want to change the base?
Conversation
Just to show how verbose is React in comparison :) class HelloTurbine extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({name, e.target.value})
}
render() {
return (
<div>
<div>Welcome from Turbine!</div>
<input onChange={this.handleChange} placeholder='Your name?' autofocus />
<div>Hello, {this.state.name}</div>
</div>
);
}
} |
Also the desugared chain version can be mentioned along and looks even simpler for this example: const main = () =>
div('Welcome from Turbine!')
.chain(() => input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
.chain(({ name }) => div(['Hello, ', name]))
} In comparison, the const reducer = (name, newName) => newName
const view = (name, dispatch) => [
div('Welcome from Turbine!'),
input({
attrs: { autofocus: true, placeholder: "Your name?" },
oninput: () => dispatch(e.target.value)
})
div(['Hello, ', name]))
] In Redux there would be even more code with all the actions needing being passed to the global state. So it is remarkable how even for that simple example, the Turbine can cut everything down to a single function. 😄 |
Although the React example is more verbose, one advantage is that the rendering of the content is completely independent of where data needs to go. For example, in the React example I can change <div>
<div>Welcome from Turbine!</div>
<input onChange={this.handleChange} placeholder='Your name?' autofocus />
<div>Hello, {this.state.name}</div>
</div> to <div>
<div>Welcome from Turbine!</div>
<div>Hello, {this.state.name}</div>
<input onChange={this.handleChange} placeholder='Your name?' autofocus />
</div> and it will work as expected. It doesn't seem so obvious how to make the same change given the Turbine example: const main = function* () {
yield div('Welcome from Turbine!')
const { inputValue: name } = yield input({
attrs: {
autofocus: true,
placeholder: "Your name?"
}
})
yield div(['Hello, ', name])
} How do I render the last div before the input? How would the example change? Also, yeah, const { inputValue: name } = yield input({
autofocus: true,
placeholder: "Your name?"
}) is nicer, cleaner, and shorter. What other things do the components accept besides |
You raise some good points. So we can rewrite the example as const main = loop(({name}) =>
div('Welcome from Turbine!')
.chain(() => div(['Hello, ', name]))
.chain(() => input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
}) Now the The order independence in the React example is not really specific to React. const main = name => [
div('Welcome from Turbine!'),
div(['Hello, ', name]),
input({
autofocus: true,
placeholder: "Your name?",
oninput: e => name = e.target.value
})
] which is, of course, impure, but neither is React with You could get a pure version with const reducer = (name, action) => action
const view = (name, dispatch) => [
div('Welcome from Turbine!'),
div(['Hello, ', name]),
input({
autofocus: true,
placeholder: "Your name?",
oninput: e => dispatch(e.target.value)
})
] which is similar to Redux but without touching the global store. Which also corresponds to the Turbine's const main = modelView(
({ name }) => name,
name => [
div('Welcome from Turbine!')
.chain(() => div(['Hello, ', name]))
.chain(() => input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
]
) Or with const main = name => [
div('Welcome from Turbine!'),
div(['Hello, ', name()]),
input({
autofocus: true,
placeholder: "Your name?",
oninput: e => name(e.target.value)
})
] where Some additional verbosity in React comes also from several other parts,
You can see some here: |
As @dmitriz already mentioned you can achieve this with the const main = loop(({name}) => div([
div('Welcome from Turbine!'),
div(['Hello, ', name])),
input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
])); Here it is easy to change the order without breaking anything.
Almost 😃 const main = modelView(
({name}) => Now.of({name}),
({name}) => [
div('Welcome from Turbine!')
.chain(() => div(['Hello, ', name]))
.chain(() => input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
]
) for better readability I would write it like this: const model = ({name}) => Now.of({name});
const view = ({name}) => [
div('Welcome from Turbine!')
div(['Hello, ', name]))
input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
];
const main = modelView(model, view); |
Thanks for correction, I was thinking about it for a second and then left it out So the perfection of this example is broken 😢 You are right, no need to chain once the parameters are taken out. |
|
I probably should have said it less cryptic :) input({ placeholder: 'name' }) without nesting under the You lose the separation of props vs attributes but |
@dmitriz I would include that desugared example somewhere in the README. That totally made sense to me in terms of category theory and demystified everything that going on in the example with generators... which begs the question -- why use generators? |
It is indeed easier to read as far as element props go. (yeah that's what he meant @paldepind) What would we do with the input({
attrs: { autofocus: true, placeholder: "Your name?" },
output: { name: 'inputValue' }
})
input({ autofocus: true, placeholder: "Your name?" }, {
output: { name: 'inputValue' }
}) so sometimes we won't need it, and the extra arg can be omitted: div({ class: "foo" }) Another option (but maybe uglier) could be special naming convention: input({ autofocus: true, placeholder: "Your name?", _output: { name: 'inputValue' } }) |
Did you see the section understanding generator functions in the readme? The section includes several desugared examples as well. We use generators because Component is a monad due to it's It's unfortunate that the generators look like "magic" but really what they do is pretty simple. We can't really avoid them since monads are no fun to use without the special syntax. One way of saying it is that generators are two monads what async/await is to promises. |
I agree that having attributes directly on the object looks nice. But as @trusktr points out there is more going on than just attributes. In fact, there's a bunch of things that one can specify on the object. For instance you can do something like this The advantage of having |
Is that an advantage because otherwise there is some danger that they clash? For instance, if I've thought that was how React is dealing with its props vs attributes by making them basically the same with no downside, is it correct? |
We can't make that assumption, we don't know what requirements the DOM has, and there's also custom elements that can have any attribute names imaginable. There could be some CSS library that requires an Props are the same as attributes in React. They map to element attributes. But React is different because JSX elements (div, input, etc, not components) only receive data via props, and they don't need any extra meta info passed, like Turbine components and The @paldepind WHat about maybe chaining the API instead of an object? const name = yield input({placeholder: 'name'})
.output('value')
.classToggle('foo', behavior)
.classToggle({bar: behavior2, baz: behavior3})
// pass `name` along here like before, for example Would that be too imperative? |
input({ autofocus: true, placeholder: "Your name?", _output: { name: 'inputValue' } }) It is an interesting idea, the props like For instance, I have found it very convenient how Angular 1 prefixed its special api methods with On the other hand, I find it confusing how React lacks to provide any hint about the special role of its methods such as |
That might be another reason to mark Another library would also likely use the flat structure :)
You can still write it as attribute as there is no other attribute with that name. |
I have tried to write the simplest possible example to illustrate the framework's value,
by showing its reactive behavior and tried to put some hopefully helpful inline comments.
Here is the brief version:
Having that kind of examples on the main page,
was how Angular got its traction, I think.
I would even prefer to replace
with the less verbose
Let me know what you think.