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