Now that it-blocks are first-class closures, could we revise the WebOutStream API to streamline Weblet code? e.g. from WebWidget fandoc:
class MyWidget : Widget
{
override Void onGet()
{
// always use head and body bufs
head.title.w("My title!").titleEnd
body.h1.w("This is my widget!").h1End
}
}
could be:
class MyWidget : Widget
{
override Void onGet()
{
// always use head and body bufs
head.title { w("My title!") }
body.h1 { w("This is my widget!") }
}
}
This has the nice side effect of never forgetting to write out matching end tags.
If you like this idea I'd be happy to do the work and send you a patch.
andyWed 15 Apr 2009
We had thought about that back when we wrote that API. And its true it would be nice in some cases - but I think it actually more verbose in practice (for me at least). Plus we would have more overhead when generating the page (though that might be a non-factor). I personally like the current API - but I'd be interested in what others thought.
tompalmerWed 15 Apr 2009
I much prefer when program structure matches the data, so I'd side with qualidafial. That said, I also much prefer to reference separate HTML template documents and elements when generating HTML, so I'm not a likely customer of this particular API, anyway.
qualidafialWed 15 Apr 2009
Another option is to just add an optional |This|? parameter to each start-tag method with a default value of null. If a block is provided then invoke it and automatically emit the matching end tag.
The upside to this approach is that you can code with or without blocks as you please and existing code continues to work as expected.
The downside is that now you have effectively two APIs for doing the same thing.
qualidafialWed 15 Apr 2009
While we're on the subject, is there a reason that the attrs argument to many of the start-tag methods is a Str? instead of a [Str:Str]? A map seems a more natural fit for this scenario even though the usage is a tad more verbose. Such a structure could be used for automatic escaping to avoid things like HTML injection attacks.
brianThu 16 Apr 2009
I think its a cool idea, one I think we've visited before during our closure debates. It lets me write declarative code like:
div
{
p { w("List of things to do:") }
ul
{
li { w("Go to work") }
li { w("Go to sleep") }
}
}
But at the same time I really like the stream/chain based API, so I don't want to loose that.
Potentially we could support both at the same time, but since all the start tag methods take an optional attributes it requires a new feature: the ability to specify a closure and omit previous default parameters. I looked at adding this feature during the it-block work and its turn out be a really big feature. In fact it is really probably part of a named parameter feature - so it probably won't happen for 1.0.
While we're on the subject, is there a reason that the attrs argument to many of the start-tag methods is a Str? instead of a [Str:Str]?
The string is actually much more readable (especially with interpolation) and immensely more efficient. We've made it really easy to escape using Str.toXml so that you can write code like:
"<xml attr='$val.toXml'>blah blah blah</xml"
qualidafialThu 16 Apr 2009
Potentially we could support both at the same time, but since all the start tag methods take an optional attributes it requires a new feature: the ability to specify a closure and omit previous default parameters.
Could we inline the default parameters at the call site?
What you said also gave me an idea for reverse curried functions
class Func
{
Func curryr(Obj?[] args)
}
The syntax for reverse curried functions could be denoted by a leading comma, e.g.
Does the current stream-based API enforce well-formedness of documents? I think that is very important. It's bad enough to have to track down missing tags in HTML. I'd rather the language help me out if it can do so efficiently.
If this validation isn't available through the current API, I'm totally in favor of qualidafial's proposal.
KevinKelleyThu 16 Apr 2009
I'm in favor too, but mainly just because it sounds elegant and I want to see how it looks.
brianThu 16 Apr 2009
Could we inline the default parameters at the call site?
Not very easily, nor do I really like that idea. I like handling on the class itself, but I want to avoid an explosion of method overloads. My thoughts for how we eventually do this is using a bitmask we can efficiently switch on (versus boxing up into a map or list).
If this validation isn't available through the current API, I'm totally in favor of qualidafial's proposal.
No, but I consider this a pretty low level API which is designed for efficiency. Lots of things are built on top of this API including the webapp::Widget API which serves a similar purpose (more declarative).
andyThu 16 Apr 2009
While we're on the subject, is there a reason that the attrs argument to many of the start-tag methods is a Str? instead of a [Str:Str]?
That actually was the original API - but it was a major pain to use. So we switched to use string interpolation, which is much easier. And I think reads better as well.
Does the current stream-based API enforce well-formedness of documents?
Not currently, though doing a simple check for #open == #closed would be pretty straightforward. However really validating an HTML document is much more complicated than that. And given that, I think its best to leave that up to the end user using HtmlTidy or the W3CValidator or something.
Having said that though, if we did do something, I think it would be more appropriate to use a WebStep. So during development, you can have a ValidateHtmlStep at the end in your boot script. And turn it off for production.
I'm in favor too, but mainly just because it sounds elegant and I want to see how it looks.
I'll have to try it out on some of my existing code to see what I think about it.
tacticsThu 16 Apr 2009
No, but I consider this a pretty low level API which is designed for efficiency. Lots of things are built on top of this API including the webapp::Widget API which serves a similar purpose (more declarative).
In that case, it might make sense to build this kind of block-oriented API on top of the existing API.
JohnDGThu 16 Apr 2009
Not currently, though doing a simple check for #open == #closed would be pretty straightforward. However really validating an HTML document is much more complicated than that.
You can enforce a lot using it blocks. For example, h1 can accept a closure for an object that has inline and text tags, but no block-level elements. Similarly, ol and ul can accept a closure for an object that has li tags, but nothing else.
You can't enforce everything, because the validity of some tags depends not on the immediate parent, but on a prior ancestor (e.g. input can occur inside a div, which is itself inside a form). However, you can at least do runtime checking for these cases.
There's certainly a nice, safe API somewhere here, backed by it blocks. Whether or not that API is or should be WebOutStream, however, is the more relevant question.
andyFri 17 Apr 2009
I think we all agree it would be nice to have a validating API somewhere. However I'm pretty confident it should not be WebOutStream - that should be left as a low-level efficient API. We could potentially have a new type in webapp you could use to optionally wrap WebRes.out or Widget.head/body to provide an it-block interface. Not sure I'm crazy about that though.
qualidafial Wed 15 Apr 2009
Now that it-blocks are first-class closures, could we revise the WebOutStream API to streamline Weblet code? e.g. from WebWidget fandoc:
could be:
This has the nice side effect of never forgetting to write out matching end tags.
If you like this idea I'd be happy to do the work and send you a patch.
andy Wed 15 Apr 2009
We had thought about that back when we wrote that API. And its true it would be nice in some cases - but I think it actually more verbose in practice (for me at least). Plus we would have more overhead when generating the page (though that might be a non-factor). I personally like the current API - but I'd be interested in what others thought.
tompalmer Wed 15 Apr 2009
I much prefer when program structure matches the data, so I'd side with qualidafial. That said, I also much prefer to reference separate HTML template documents and elements when generating HTML, so I'm not a likely customer of this particular API, anyway.
qualidafial Wed 15 Apr 2009
Another option is to just add an optional
|This|?
parameter to each start-tag method with a default value of null. If a block is provided then invoke it and automatically emit the matching end tag.The upside to this approach is that you can code with or without blocks as you please and existing code continues to work as expected.
The downside is that now you have effectively two APIs for doing the same thing.
qualidafial Wed 15 Apr 2009
While we're on the subject, is there a reason that the
attrs
argument to many of the start-tag methods is aStr?
instead of a[Str:Str]
? A map seems a more natural fit for this scenario even though the usage is a tad more verbose. Such a structure could be used for automatic escaping to avoid things like HTML injection attacks.brian Thu 16 Apr 2009
I think its a cool idea, one I think we've visited before during our closure debates. It lets me write declarative code like:
But at the same time I really like the stream/chain based API, so I don't want to loose that.
Potentially we could support both at the same time, but since all the start tag methods take an optional attributes it requires a new feature: the ability to specify a closure and omit previous default parameters. I looked at adding this feature during the it-block work and its turn out be a really big feature. In fact it is really probably part of a named parameter feature - so it probably won't happen for 1.0.
The string is actually much more readable (especially with interpolation) and immensely more efficient. We've made it really easy to escape using
Str.toXml
so that you can write code like:qualidafial Thu 16 Apr 2009
Could we inline the default parameters at the call site?
What you said also gave me an idea for reverse curried functions
The syntax for reverse curried functions could be denoted by a leading comma, e.g.
tactics Thu 16 Apr 2009
Does the current stream-based API enforce well-formedness of documents? I think that is very important. It's bad enough to have to track down missing tags in HTML. I'd rather the language help me out if it can do so efficiently.
If this validation isn't available through the current API, I'm totally in favor of qualidafial's proposal.
KevinKelley Thu 16 Apr 2009
I'm in favor too, but mainly just because it sounds elegant and I want to see how it looks.
brian Thu 16 Apr 2009
Not very easily, nor do I really like that idea. I like handling on the class itself, but I want to avoid an explosion of method overloads. My thoughts for how we eventually do this is using a bitmask we can efficiently switch on (versus boxing up into a map or list).
No, but I consider this a pretty low level API which is designed for efficiency. Lots of things are built on top of this API including the webapp::Widget API which serves a similar purpose (more declarative).
andy Thu 16 Apr 2009
That actually was the original API - but it was a major pain to use. So we switched to use string interpolation, which is much easier. And I think reads better as well.
Not currently, though doing a simple check for #open == #closed would be pretty straightforward. However really validating an HTML document is much more complicated than that. And given that, I think its best to leave that up to the end user using HtmlTidy or the W3CValidator or something.
Having said that though, if we did do something, I think it would be more appropriate to use a WebStep. So during development, you can have a ValidateHtmlStep at the end in your boot script. And turn it off for production.
I'll have to try it out on some of my existing code to see what I think about it.
tactics Thu 16 Apr 2009
In that case, it might make sense to build this kind of block-oriented API on top of the existing API.
JohnDG Thu 16 Apr 2009
You can enforce a lot using it blocks. For example, h1 can accept a closure for an object that has inline and text tags, but no block-level elements. Similarly, ol and ul can accept a closure for an object that has li tags, but nothing else.
You can't enforce everything, because the validity of some tags depends not on the immediate parent, but on a prior ancestor (e.g. input can occur inside a div, which is itself inside a form). However, you can at least do runtime checking for these cases.
There's certainly a nice, safe API somewhere here, backed by it blocks. Whether or not that API is or should be WebOutStream, however, is the more relevant question.
andy Fri 17 Apr 2009
I think we all agree it would be nice to have a validating API somewhere. However I'm pretty confident it should not be WebOutStream - that should be left as a low-level efficient API. We could potentially have a new type in webapp you could use to optionally wrap
WebRes.out
orWidget.head/body
to provide an it-block interface. Not sure I'm crazy about that though.