Ur/Web FAQ
Where do I find plain "Ur" without the "Web" part?
I describe the system as if it is a language plus an extra library, but, in reality, no separate Ur implementation exists. The Ur/Web language does have a clear core that is independent of web stuff, and that's what I mean by plain "Ur."
What are the main benefits of Ur/Web over competing tools?
Ur/Web makes programmers more productive by providing abstraction and modularity features not found in any other language.
Ur/Web provides guaranteed security; certain kinds of security vulnerability are impossible. For example, modulo any compiler bugs, any Ur/Web application that the compiler accepts is free of code injection vulnerabilities.
Ur/Web provides much better run-time performance than any competing system. Server-side components are native code and don't use garbage collection.
Why might I prefer to use a different tool?
Ur/Web is designed for people who get along well with statically typed functional programming. If you don't like Haskell or ML, then you probably won't like Ur. The documentation only tries to explain concepts that won't already be familiar to Haskell and ML experts.
Which unusual abstraction and modularity features does Ur/Web include?
There is the old bulwark of first-class functions with lexical closures, which most languages in this space already support, though there are some laggards.
From ML, Ur/Web takes a module system with signatures and functors, supporting type abstraction.
From Haskell, Ur/Web takes type classes and monads, including a monad for containing side effects.
From the world of dependent types, Ur/Web takes type-level computation. This makes it possible to express much richer types that, in a sense, allow you to describe the behavior of a function with a simpler function that lives at the level of types.
Ur/Web takes the ML module system further and allows encapsulation of the high-level pieces of web applications inside modules. Similar encapsulation is possible within function definitions. The Ur/Web compiler guarantees that an entity "owned" by a module or function can only be accessed by going through the owning module/function. Examples of first-class "ownable" pieces are cookies, CSS classes, database relations, web page generators, AJAX request handlers, and subtrees of the DOM trees of particular pages.
Type-level computation with first-class names and records enables a very effective kind of metaprogramming, while maintaining expressive static types for metaprograms.
What other nifty features are there?
Write server-side and client-side code in the same statically typed language. The compiler automatically compiles server-side code to native code and client-side code to JavaScript.
All marshalling and unmarshalling is performed automatically. This includes cases that show up in normal page generation, form handlers, AJAX requests and responses, Comet-based message-passing, database interaction, and quoting of code being generated by code.
Client-side GUI programming via an interface inspired by functional-reactive programming, where a page is described as a pure function over a set of mutable data sources. When a data source changes, the affected parts of the page update automatically.
Client-side cooperative multi-threading
Easy AJAX requests, where client-side code just calls a server-side function directly, and Ur/Web takes care of implementing this over HTTP.
Easy Comet, where a typed message-passing system is implemented on top of the usual long-polling architecture. To Ur/Web programs, it looks like the server is able to send messages asynchronously to clients.
Most of standard SQL supported as a strongly typed library. Tables are defined inside Ur/Web modules, and table definitions may include SQL constraints that are computed programatically.
Simple foreign function interface for connecting with C or JavaScript code.
Which classes of security vulnerabilities are ruled out automatically?
All of the following are only true up to the lack of bugs in the compiler, but, with a correct compiler, you shouldn't need to worry about accidentally writing an application that's susceptible to any of them.
Buffer overflows should be impossible, as in all type-safe languages.
Vulnerabilities arising from automatic run-time interpretation of strings as code should be impossible. You won't get any interpretation of strings unless you write an interpreter yourself! All of the following have special static types that guarantee well-formedness: HTML, SQL, URLs, MIME types, CSS class names. A whitelist of allowable patterns may be specified in a central place for URLs and MIME types, making it impossible for applications to contain links via dangerous protocols or return file data with dangerous MIME types (like any kind of code that the browser might run). This simple no-interpretation principle rules out the first 3 of OWASP's Top 10 2007 vulnerabilities: cross site scripting, injection flaws, and malicious file execution.
Most kinds of cross site request forgery are impossible. Ur/Web detects which pages could both read cookies and cause side effects. Any request for such a page automatically contains a cryptographic signature of all cookies that might be read. Ur/Web currently doesn't support any kind of persistent client state besides cookies, so some fairly serious client-side acrobatics would be needed to circumvent the signature scheme.
Can you be more specific about run-time performance?
The TechEmpower Web Framework Benchmarks provide a performance comparison managed by a third party. Ur/Web does pretty well, and you can check that site for details, but a few caveats are important. First, the Ur/Web programming model is unusually oriented toward security and concurrency simplicity. For instance, Ur/Web's standard random number generation function generates cryptographically secure numbers, which imposes an extra run-time cost in the several benchmarks based on random numbers; and Ur/Web's concurrency model allows the programmer to think of every piece of code as running inside a transaction, which imposes extra run-time cost in the several benchmarks that use databases, necessarily within a transaction per request, unlike in almost all other frameworks' entries in the benchmarks.
OK, having said all that, Ur/Web is still doing pretty darn well! Consider the Round 11 results for the highest-capacity machine in the benchmarks, which provides 40 hardware threads. In rough numbers, here's how Ur/Web is doing.
Test | Requests/sec. | Avg. Latency |
Fetch from database & render as HTML | 300k | 0.8 ms |
Hello world in JSON | 400k | 0.6 ms |
1 SQL query | 160k | 1.5 ms |
20 SQL queries | 10k | 22 ms |
20 SQL query/update pairs | 600 | 2 s |
Yeah, Ur/Web is really falling over in the many-updates test, with SQL concurrency control thrashing to provide the transactional semantics that most benchmark entrants don't bother to shoot for. Still, very few web sites process as much as hundreds of requests per second!
Despite Ur/Web's performance handicaps in service of a pleasant programming model, on the Fortunes test (listed first in the table), which is closest to the scenario Ur/Web was designed for, Ur/Web has the 2nd-best throughput and is tied for best average latency, out of about 100 framework configurations passing that test's basic sanity check. Ur/Web is kneck-in-kneck here with ULib, a C++ framework. The 3 other C++ frameworks participating all fail to come much closer than 50% of Ur/Web's throughput. In the spirit of sibling rivalry, I'll point out that the Haskell frameworks also consistently achieve less than half the throughput of Ur/Web on tests that use the database.
Why would I prefer to use Ur/Web over [insert name of favorite Haskell web framework]?
There are some less serious advantages of using a DSL with integrated compiler support, but the main advantage of Ur over Haskell is a more expressive type system, supporting type-safe metaprogramming. Consider how you might implement Haskell libraries like those behind the CRUD demo or the in-browser spreadsheet demo, while retaining the same level of static guarantee about a library independently of the applications it winds up being used in. I don't think this challenge could be met without using overlapping type class instances, and even then I expect that the code would be substantially uglier and more complex than in Ur.
Why would I prefer to use Ur/Web over OPA?
- OPA has a more or less traditional ML-style type system, while Ur's is much more sophisticated, permitting statically typed metaprogramming and more.
- The current version of OPA integrates a navigational database, with no strongly typed SQL interface, while Ur/Web focuses on SQL for persistent storage.
- OPA does client-side page manipulation through traditional imperative DOM access, while Ur/Web uses a functional reactive model, which allows for encapsulation schemes that are impossible in OPA (and with traditional JavaScript idioms).
- At least in the recent past, the Ur/Web compiler has produced much more efficient binaries than the OPA compiler.
You really expect me to go back to the bad old days of writing SQL queries instead of using an ORM?
Personally, I find direct SQL much more pleasant than ORM, as I prefer declarative interfaces to alternatives in most situations. If you're into ORM, you can implement type-safe ORM libraries like this example completely within Ur/Web.
You really expect me to go back to the bad old days of writing HTML directly in app code instead of using templates?
You have no fewer choices than in other languages: Templates may be implemented as functions within Ur/Web. Examples of this may be forthcoming if enough noise is made about it!