I have been working on getting a sample for using XPO from F#. My first sample was easily created back in January this year:
#light
open DevExpress.Xpo
type Person = class
inherit XPObject as base
public new(session : Session) = { inherit XPObject(session);
name = string.Empty
}
val mutable private name : string
member public x.Name
with get() = x.name
and set(v) = x.name <- v
end
let person = new Person(XpoDefault.Session)
person.Name <- "Wally"
person.Save()
This works just fine — great. Now I wanted to create a slightly more evolved sample using more of the standard XPO features, and use that to develop some best practices. I also had the idea to create some CodeRush templates similar to those we have for C# and VB. All this turned out much more complicated than I thought, for a variety of different reasons.
The first issue I stumbled upon was that I can’t call our standard helper method SetPropertyValue correctly from F#. It looks like a bug to me, since I did a lot of very similar tests, and only with the precise combination of parameters and return type that this method has does the F# call to it fail. If it’s confirmed to be a bug, it’ll get fixed — but so far I haven’t heard anything, and I want a workable solution now.
I started working on one — see below — but first I had to decide which type of mutable fields I was going to use for my persistent classes. F# allows class fields to be declared “mutable”, which means they can be changed (if this sounds like an ordinary thing to you, I recommend you read up on your functional programming theory!). It also allows to declare values as “ref cells”, which wraps the actual values in an additional class internally. Our standard SetPropertyValue
method uses a ref
parameter in its C# implementation. Of course that method isn’t working anyway, so I was considering writing my own helper method for the time being. C# ref
parameters are not something F# deals with extremely gracefully in my opinion, the main reasons for which are probably that changeable values are not foremost in peoples’ minds in that language, and there’s support for multiple return values using tuples, so the use cases from ref
parameters are much reduced anyway.
The only way (afaik - a topic I’m still a little unsure about) that F# ever passes a parameter by reference is if the value I’m using is a ref cell in F#. The problem with using multiple return values instead of the by-reference type method, whether we’re talking about the standard helper method or a new one, is that it changes the requirements of the syntax at the call site. Optimally, I would like the helper API to be very easy to use. I also want to perform change notification from within the helper method. Using multiple return values, the pattern could look like this:
let setValue oldVal newVal =
((oldVal <> newVal), newVal)
let testSetValue1 newVal =
let mutable testval = 10
// This is the line I'd need in my property setters
match (setValue testval newVal) with | true, \_\_n -> testval <- \_\_n | _ -> ()
printfn "testval: %d" testval
testSetValue1 10
testSetValue1 42
There are several things wrong with this. First, the line I need to use in each property setter is rather complicated. I’d integrate it in a template, but there are people who don’t use templates, and anyway it just looks wrong to reproduce that sort of thing in every single property. Second, the naming of the helper function is really no longer correct — it doesn’t actually set the value of the property anymore, it just finds out whether setting the value is required at all. Of course if that’s all I want to do, I should change the function’s name and I don’t need multiple return values after all… the real problem is that the actual setting of the value happens in the property setter code. And that brings me to third: if I was to integrate change notification in the helper function, this would now happen before the property change actually takes place (and that’s still assuming that it takes place at all, which is not under the control of the helper function anymore).
So all this looked bad to me and I decided to go with the by-reference type helper function after all. This works only if the value I’m actually setting from inside the helper function is a ref cell. In C#, it’s possible to take any given variable and pass a reference to it into a method that requires such a reference. Not so in F#, apparently. Unless a value is declared to be a ref cell, it can’t be used in this way. Declaring as a ref cell in turn means that there’s an extra wrapper class created for the value — quite a bit of unnecessary overhead it seems, particularly when dealing with large numbers of fields such as there are typically in persistent classes. But since ref cells are the only way to make the F# compiler pass something by reference, there’s no way around that. I did some additional tests with temporary ref cells and other things, but this only made the overhead worse, not better. So I decided to accept things as they are and move on.
One other thing I should mention about ref cells: they require special syntax when used in code, basically they need to be dereferenced in order to access the actual value. So instead of writing foo
to access the value foo, I now have to write !foo
(yeah, looks like not foo
, but it’s not, if you know what I mean). Quite inconvenient as well, if you write a lot of business logic in your class and prefer to use the private field directly. Probably better to use the public properties where possible, then you don’t have that problem. My next step was to try and pull the helper functions which can’t be called in their original C# implementation into F# as a workaround. That looked pretty simple to begin with — our C# implementation is actually quite a bit more complex because we have to jump through some hoops to get the equality comparison right (that’s the reason for having the conflicting overloads I mentioned earlier). I created a base class that I was going to use for further objects:
type FSharpBaseClass =
inherit XPObject as base
public new(session: Session) = { inherit XPObject(session) }
member public x.SetPropertyValueFS propertyName oldVal newVal =
let needsSetting = !oldVal <> newVal
if needsSetting then
oldVal := newVal
x.OnChanged(propertyName, !oldVal, newVal)
needsSetting
And lo and behold, here’s the next inexplicable F# issue: I can’t call the OnChanged method! Again, no idea why that is — I did a bunch of tests with very similar setups and all of these work. OnChanged
can’t be called though, I get a compiler error:
Error 5 A protected member is called or a base variable is being used. This is currently only allowed in the direct implementation of members since they could escape their object scope.
Of course there’s not a single page that Google can find about this problem, that would have been boring. Ah well. That’s it for today in any case. To be continued.