views:

298

answers:

1

I'm extending Fluent NHibernate for better use with F# (namely, quotation support), and want some feedback on de-fluenting the API. F# requires that return values be used, unless they are type unit. So this ends up terminating every line with "|> ignore":

type ProductMap() as m = inherit QClassMap<Product>() do
    let x = Unchecked.defaultof<Product> 
    m.Id <@ x.Id @> |> ignore
    m.Map <@ x.Name @> |> ignore
    m.Map <@ x.Price @> |> ignore
    (m.HasManyToMany <@ seq x.StoresStockedIn @>)
        .Cascade.All()
        .Inverse()
        .WithTableName("StoreProduct") |> ignore

My first reaction was to add more methods to the base class so they return unit. For instance, "IdI" and "MapI":

...
m.IdI <@ x.Id @>
m.MapI <@ x.Name @> 
m.MapI <@ x.Price @> 
...

But that requires specific overloads here and there, and long chains are still going to need a |> Ignore. I also considered extending object with a Done property:

(m.Id <@ x.Id @>).Done
(m.Map <@ x.Name @>).Done
(m.Map <@ x.Price @>).Done
(m.HasManyToMany <@ seq x.StoresStockedIn @>)
    .Cascade.All()
    .Inverse()
    .WithTableName("StoreProduct").Done

What do you think?

+3  A: 

IMHO a better approach would be to start from scratch thinking in F# (e.g function piping, currying, combinators) instead of wrapping fluent nhibernate, but using what fluent nhibernate has used to generate the mappings. That is, building a "parallel fluent nhibernate" for exclusive use of F#.

I've recently posted about a similar issue with Windsor's fluent interface in F#. My conclusion is that many DSLs / fluent interfaces built for C# / VB.NET will break in F# so I think it's best to build specific fluent interfaces that suit the F# way.

Mauricio Scheffer
Yea... but that's a lot of work ;). I'll play with idea, thanks!
MichaelGG
I know... but would you use NUnitEx (http://code.google.com/p/nunitex/) in F#, or would you try and build something like FsUnit (http://code.google.com/p/fsunit/) ?
Mauricio Scheffer
DSLs / fluent interfaces are just not very portable across languages... for example I'd never expect to be able to run a Boo DSL in C# even though it runs in the same platform... so it's the same thing with F#
Mauricio Scheffer
Well, perhaps in some cases it's more pronounced. But FNhibernate works fine except for the |> ignore, and I don't see how that'd go away.
MichaelGG
Agreed, it works. But it loses some of its fluency... about |> ignore, you could avoid it by inverting the order of each mapping, i.e. (pseudocode) <@ x.Name @> |> map (/pseudocode)
Mauricio Scheffer
I'd also try using a computation expression (aka monad) to define the mapping, it could reduce cruft considerably
Mauricio Scheffer
Take a look at FParsec (http://www.quanttec.com/fparsec/) for inspiration
Mauricio Scheffer
related, more general, post about the subject of VM/language portability: http://sadekdrobi.com/2008/06/01/and-you-get-all-the-vm-libraries-for-free-is-it-actually-what-i-want-when-i-switch-languages/
Mauricio Scheffer