Introducing furious – a proof-of-concept RRM for F#
Cats: Uncategorized
One of the first things that newcomers to F# look for is an ORM system, be it a native one, or an adaptation of something already present. Since there are plenty of systems in the .net ecosystem, it is possible to get one of them to work with F#. But as we start to do that, the more seasoned f#'er will notice that something is amiss. One of the core tenets of F# is immutability, and classic ORMs rely on mutability by operating on class properties. While F# does support classes with mutable properties, the preferred unit of work is the record. This also follows the representation of the database better - at any one point in time you're either holding an uncommited record, or a representation of database state; immutability there prevents any confusion. These considerations, and discussions with some of the F# folks out there (Aaron Erickson being a major influencer of this idea), I decided to put something together meant for F# specifically.
Meet furious. Furious is a RRM - a Record Relation Mapping system. It is a database-independent object query dsl for records, that currently has a (barebones) mysql implementation. Here's how it works. Suppose you have a record graph like so:
personId: string
firstname: string
lastname: string
homeAddress: address
workAddress: address
altAddresses: address seq
}
and address = {
addressId: string
street1: string
zip: string
}
Now, if i wanted the list of all people that live in the 60614 zipcode, i could write this:
db.Yield <@ Seq.filter (fun p -> p.homeAddress.zip = "60614") @>
That's it. Neat, huh? Now, if you wanted something like a count of all such neighbors, you could do this:
db.Compute <@ fun people -> Seq.length <| Seq.filter (fun p -> p.homeAddress.zip = "60614") people @>
The main idea behind furious is to avoid inventing a new dsl, and use what's already there - the concepts defined over the Sequence space. Furious provides two methods - Yield (expr: Expr<(seq<'a>->seq<'b>)>), and Compute (expr: Expr<(seq<'a>->'b)>). The idea is that one is for mapping one sequence into another, and the other is for computing a value from a sequence (such as our Seq.length example). The interesting thing is that this approach is enough to cover a majority of all orm/rrm usecases. While there are statements that can't (easily) be expressed this way, they reach a complexity level that should really be done via explicit sql, rather than this type of generation.
Furious is currently a proof-of-concept project that will evolve into a full library. It currently lacks a lot of major features, and has only been tested for a handful of scenarios. That said, I welcome input and contribution, as this is a complex project that needs more than one mind.
The code is located here: http://github.com/kolosy/furious