The links to the actual web applications will only work if you are running them locally, and also probably only if you are me.
Laconic demo
>:O says: I write the same boilerplate code for every piece of every web application I ever create. When I use Ruby on Rails to auto-generate some of this code and modify the standard templates incrementally, I end up with a mess of copy-and-paste "code re-use." I can't take advantage of later improvements to code generation libraries, and the new code of my application is a sprawling mess!
8^) says: No problem. Laconic uses the magic of type theory to solve every problem known to man. The code you write is more or less exactly the high-level specification you have in mind.
The big picture
8^) says: Here's an example of using a library for the clean generation of "CRUD" code for arbitrary database tables. CRUD stands for Create/Read/Update/Delete, and most web applications have hand-implemented functionality like that for every table. With Laconic, a statically-typed library handles the creation of this code. We'll get to how this library is implemented by the end of this demo.
person.lac -- Application-specific code
person.lab -- "Makefile" equivalent
Basic web page stuff
>:O says: All these new-fangled "web application frameworks" generate complicated directory structures of standard files before I get to write "Hello World!" to a web page.
8^) says: Laconic makes this just as easy as writing the page statically, and checks at compile time that your HTML is valid, too.
hello.lac
>:O says: I have to toil over a hot keyboard all day coding the logic that connects web requests to particular server-side handlers... or, if I use Ruby on Rails or one of its relatives, I have to mangle my idea of an application's structure so that every action matches up with a method of a class. With Rails, my code ends up strewn across several files when what I want is to look at a single piece of code that makes it clear exactly what is going on.
8^) says: Laconic uses closures to handle "control-flow" kinds of state as easily as you can imagine.
link.lac
mutual.lac
>:O says: When I use HTML forms, I find myself in a prehistoric world of dynamic scoping, having to remember the names and types of the form fields so that I can unmarshal them manually from text in form handlers.
8^) says: Laconic checks your use of forms statically and avoids all marshalling and unmarshalling. The type of a page generator makes it clear what form fields it expects and what their types should be. Also, all uses of these user inputs are validated with static checking. There's no need to remember to "escape a string" in any context.
form.lac
SQL
>:O says: Oh, and don't get me started about SQL! Somehow, despite the fact that embedded SQL has been around since almost the beginning, everyone seems to be building and compiling SQL queries from strings at runtime. Oh, or they're using object-relational bindings, which bring the exciting clunkyness of OO programming to the elegant world of relational databases.
8^) says: You're in luck. In Laconic, SQL queries have nothing to do with strings. SQL queries are a first-class, strongly-typed part of the language. They're checked and compiled statically. SQL injection attacks are literally impossible.
select.lac
fancy.lac
Metaprogramming
>:O says: OK, all that stuff is all right, but all it means is that the amount of boilerplate code that I'll be writing is decreased by a constant factor. What was that you claimed about big savings in code size?
8^) says: I stand by my claim, and the secret is metaprogramming; that is, programs that write programs. Actually, it's implemented as partial evaluation for a very expressive source language, where it's a compile-time error if particular language constructs can't be partial-evaluated away. All of the features I've already told you about are more complicated than they seemed, since they're designed be constructed programmatically while still supporting strong static typing.
The fundamental kind of redundancy in web applications is rewriting the same functionality to work with SQL tables of different types. Because of this, Laconic gives row types (or extensible records) a special status. The interesting parts of metaprogramming are performed through folding over row types.
adder.lac -- Implementation
adder.lig -- Interface
adderClient.lac -- Client
>:O says: That's great. I always wanted to sum the elements of a record.
8^) says: Don't quit your day job.... We can now revisit the features I showed you before and see how they interact seamlessly with metaprogramming. Let's start with a generic library for building a class of form-based web apps.
metaform.lac -- Implementation
metaform.lig -- Interface
metaformClient.lac -- Client
>:O says: I'll give you a call when I write a web application dealing with sequences of integers.
8^) says: Now, don't panic. It's easy to handle a wide variety of atomic data items using existential constructors. Unlike standard existential types from type theory, these exist only at the type level and are specialized out during compilation.
whatever.lac -- Implementation
whatever.lig -- Interface
whateverClient.lac -- Client
>:O says: And what about SQL queries? Writing those over and over is no fun, either.
8^) says: Yeah, we've got those, too!
metaquery.lac -- Implementation
metaquery.lig -- Interface
metaqueryClient.lac -- Client
Back to the big picture
8^) says: Here's the implementation of the CRUD library, which draws on all of the features I've described.
crud.lig -- CRUD library interface
crud.lac -- CRUD library implementation