How To Dynamic Web Rendering?

Jacob Carlborg doob at me.com
Sun May 15 06:26:48 PDT 2011


On 2011-05-13 17:19, Adam D. Ruppe wrote:
> Jacob Carlborg wrote:
>> How is it working out with a static type system, compared to a
>> dynamic, for web development?
>
> It's *much* better, especially for rapid development. The compiler
> will tell me if my changes anywhere break things anywhere else,
> so I can modify with confidence.
>
> I can't tell you how many times I've made a minor mistake in PHP,
> Ruby, or Visual Basic and had it slip through to production because
> the code path was obscure enough to evade testing.

That's what I really miss when doing web development in a language with 
a dynamic type system. It's hard to do any refactoring, always the 
possibility to break some code you had no idea would be affected.

> Sometimes, something similar does happen in D - to!int("") throws -
> but, the compiler catches the majority of incompatible changes
> instantly.
>
> If I had int addUser() and change it to User addUser(), I don't
> have to hunt every instance down myself. Just hit compile and
> the compiler tells me where the change causes problems. (Rarer than
> you might think - thanks to auto, a lot of the program is already
> duck typed in a way. The actual return value name is rarely used.)
>
>
> It also helps documentation to have good types. I often see stuff
> like this in dynamic language docs:
>
> function doSomething(options);
>
> What does it return? What options are available? Maybe the text
> will tell us that options is an associative array. UGH, I hate
> that! Now, there's even less knowing what's going on.

Actually that's one thing I really like with Ruby and Rails, that you 
can put different types of values in arrays and hashes. But I can agree, 
sometimes it's hard to know what options are available.

> D's documentation is a lot more helpful, and easier to keep up to
> date.
>
> string doSomething(Options options); // not much better yet....
>
> struct Options;
> int speed;
> bool strictMode;
> bool caseSensitive;
>
>
> Ahhh, now I know!

But that way you would need to declare different types of Options 
structs all over the place?

> Another thing that the static types enable that I don't think is
> possible with dynamic functions is the automatic forms you see
> on my link.
>
> http://arsdnet.net/cgi-bin/apidemo/get-a-box
>
> I didn't write any html for that. The library made it for me.
>
> In the source, you can see it's prototype:
> http://arsdnet.net/apidemo.d
> 	Element getABox(Color color);
>
>
> The reflection was able to say "it needs a Color, and there's only
> a handful of valid Colors - enum Color - so I can automatically
> create a<select>  box for this."
>
> Dynamic types for return values can still do most the stuff my
> api generator does. Whether it's a static function/template toHtml
> or a dynamic property doesn't matter much.
>
> But the parameters... I don't think it can be done without the
> static types there.
>
> (Another nice thing is the library checks the type too. Coming
> off the request parameters, you have strings; dynamic types. But
> going into the function, it knows it must be a Color, so it
> checks. If not, an exception is thrown.
>
> By the time you're inside the function, you can be guaranteed that
> Color color is indeed a Color. Thus, to!string() to insert it into
> the html attribute that I did here is perfectly safe. It's a
> whitelist based filter, all done automatically.)
>
>
>
> There are a few cases where dynamic types are helpful or needed.
> D lets us opt in to them with only a minimal amount of fuss.
>
> The places where I use it are:
>
> 1) Data from the user. Form variables are of type string coming
> into the program. Once decoded, you have string[][string]. Your
> program probably wants something else.
>
> The to!() template in Phobos makes this pretty easy, or if you
> want default value and get/post combined, my cgi class has a request
> template:
>
> int a = cgi.request!int("a");
>
> That's similar to this in PHP:
>
> $a = 0;
> if(isset($_POST['a']))
>    $a = (int) $_POST['a'];
> else
> if(isset($_GET['a']))
>    $a = (int) $_GET['a'];
>
>
> PHP is so outrageously verbose. And the PHP is buggy compared to
> the D... if you do cgi.request!int("a", 10) and the user does a=txt,
> the PHP result is wrong. It's not quite fair to compare a function
> to a code collection though. A php function could do it right too.

I think there are other languages available that is far better than PHP 
that you could compare with instead. Ruby on Rails:

a = params[:a].to_i

Depending on what you need this might not be the best way to write it. 
If :a doesn't exist in "params" it would return "nil" and "nil.to_i" 
would give you 0. If you use the Rails methods it will to the right 
thing most of the times without you need to worry about it. For example:

Post.find(params[:a])

This will try to find a Post with the given id and automatically 
converted the given value to an integer. If it fails to convert the 
value to an integer it will return 0 (this is not the best solution and 
I don't like it).  When it can't find a Post with the given value (you 
most likely won't have a an id in the database with the value 0) it will 
through an exception.

> Annnnyway, I'm digressing. Hell, I'm going on about weak types
> instead of dynamic types! Even in Ruby, you'd have to do a to_i
> since the url value simply is a string.
>
>
>
> Back to dynamic types. The three other places where you see them
> are databases and interfacing with external services.
>
> 2) Databases
>
> With databases, you probably expect a certain type anyway. If the
> column is typed as INTEGER, you probably don't want 'lol' in there.
> But, the database takes care of that for you.
>
>
> In my libs, I just use strings.
>
> auto line = mysql.query("SELECT id FROM users WHERE id = 10").front;
>
> res[0] == "10"
>
>
> Conversions are now done the same as URL parameters:
>
> int id = to!int(res[0]);
>
>
> Oftentimes, I don't really care about it though; if I'm outputting
> it to html, I'll just say:
>
> table.appendRow(res["id"], res["name"])
>
> and the like anyway. You want a string there, so it just works.
> (appendRow is in fact a variadic template so it'd just work
> regardless, but you get the idea anyway).

I've been thinking for a while to try and create something similar as 
Rails' ActiveRecrod for D but every time I tried to do it turns out to 
work quite badly with a static type system. Either you would need to 
specify the columns and the types in the model class (which I would like 
to avoid) or you would have to use Variants all over the places. And if 
you use Variants all over the place then I don't so much reason in using 
a language with a static type system compared to a dynamic type system.

> Thanks to static checks though, if I were to later change my mind
> and wanted ints, or made the database return Variants or whatever,
> no problem - the compiler will point out trouble spots to me.
>
>
> Inserting data is the same:
>
> mysql.query("INSERT INTO users (id) VALUES (?)", 10); // works
> mysql.query("INSERT INTO users (id) VALUES (?)", cgi.post["id"]); // works too
>
>
> Or using the data object class:
>
> auto obj = new DataObject(mysql, "users");
>
> obj.id = 10; // works
> obj.id = "10"; // works just as well
>
> obj.commitChanges();
>
> (The DataObject can read too, offering database fields as obj.id
> style properties. They always return string, just like plain query,
> but are also read/write.)
>
>
> DataObject can also be specialized automatically to use static
> types if you like.
>
> db.sql:
> create table users(id integer primary key, name varchar(30));
>
> file.d:
>
> // parse out that create table to map the names and types automatically
> alias DataObjectFromSqlCreateTable!(import("db.sql"), "users") User;
>
> User a;
>
> a.id = "12"; // type mismatch, fails to compile
> a.name = "hello"; // works
> a.nmae = "sad" // typo caught at compile time
>
> a.commitChanges(); // otherwise it is still based on DataObject
>
>
>
> In short, I have the best of both worlds, with the only extra effort
> being writing to!() here and there and that one line to import the
> sql definition.
>
>
> 3) HTML
>
> I *want* strict typing when working with html in almost all cases.
> It helps catch encoding bugs.
>
> The one place where I want dynamics - fetching an element off
> the html template - I have it, this time provided through classes.
>
> auto table = cast(Table) document.getElementById("data-table");
> assert(table !is null); // fails if it isn't a<table>  tag
>
> // use the table class's special functions
>
>
> Forms, Links, and other types of nodes are handled similarly. The
> Element base class though does most the simple operations. Being
> plain old inheritance, the virtual functions act dynamically
> enough.
>
> Of course, if the HTML changes, that assert/enforce will trigger
> at run time. We're still ahead of dynamic languages though: the
> compiler will tell me what breaks if I remove the cast, with one
> exception: I use an opDispatch there for attributes. That will
> break some functions.
>
> Table t = ...;
>
> t.caption = "Hello!"; // changes the<caption>  tag child
>
> Element t = ...;
>
> t.caption = "Hello!"; // sets a caption="" attribute
>
>
> But meh, in this case, I like the flexibility of accessing
> attributes like that. The compiler still catches more than it
> doesn't - we're still ahead of the mistakes possible in dynamic
> languages while keeping the convenience.
>
>
> 4) External services
>
>
> This is where dynamic types are the most useful. Accessing Facebook,
> Twitter, etc., returns xml or json with types that change based
> on a lot of things. Is the user logged in? What url is it?
>
> In this case, it's like the database, but the types tend to be more
> complex. Simple string cowboying won't cut it.
>
> Thankfully, D's Variant is up to the task.
>
>
> Variant userRet = fbGraph(token, "/me");
>
> // we know /me returns a json object from the docs, so fetch it:
>
> auto user = userRet.get(Variant[string]);
>
> auto name = user["name"].get!string;
>
> // name is a string == "Adam D. Ruppe"
>
>
> Not terribly hard. Robert Jacques IIRC has written an improved
> std.json patch to make this work almost just like Javascript:
>
> auto user = fbGraph(token, "/me");
>
> user.name.get!string; // same as above
>
>
> So D's definitely up to the job. The only difference is that final
> use of get!string. I believe Phobos' to! works as well.

As I said above, I don't see the big advantage if I have to use Variants 
all over the place .

> In the end, there's nothing web related in my experience that a
> dynamic language can do that D can't do almost or just as well,
> and plenty that D and it's static types can do that dynamic types
> can't do, or require piles of painful tests and manually written
> code or documentation to do.
>
>
> D kicks the Web's ass. (And my web related libraries go even further
> than seen here. My custom DOM offers a lot of things that are way
> cool, enabling new CSS stuff, filling forms easily, in about 1/10
> the code of doing it similarly in PHP, and without mucking up the
> HTML. The web api/site generator makes D accessible through urls,
> forms, javascript - view the source here for a glimpse of that
> lib:
> http://arsdnet.net/cgi-bin/apidemo/javascript
>
> 20 kb of javascript, including the auto generated API bindings!
>
> And more stuff is packed in there too. Best of all: D, through
> CGI, consistently does well or better than mod_php in my speed
> benchmarks. I've barely tried to optimize too; there's a lot of
> untapped potential.
>
>
> If your host doesn't suck, dive into D on the web. You won't want
> to go back.)

I don't know what your experiences are but since you mention PHP a lot I 
can agree with you, PHP sucks. Compared to PHP you can do a lot better, 
also with a dynamically typed language. Actually all the "native" 
languages in web development (HTML, CSS, JavaScript) are quite horrible. 
I don't know if it's just me but I try to hide those native languages as 
much as possible. I'm currently now uses Ruby on Rails with the 
following languages:

* Ruby as the server side script (obviously)
* HAML, generates HTML - http://haml-lang.com/
* SASS, generates CSS - http://sass-lang.com/ (I'm using the sass syntax 
and not the scss syntax)
* CoffeeScript, generates JavaScript - 
http://jashkenas.github.com/coffee-script/

Every language is compiled to its native format automatically when 
needed and cached in production. I just tried Rails 3.1 beta yesterday 
and now SASS and CoffeeScript are default. I think these languages make 
web development a less of a pain, but in the end they all compile to 
their native languages and there is no way to get away from that.

-- 
/Jacob Carlborg


More information about the Digitalmars-d-learn mailing list