Oddities in F#/C# interaction

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 <g>). 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 <g>). 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.

7 Comments on Oddities in F#/C# interaction

  1. FYI, you can pass ‘by ref’ using the prefix ‘&’ operator on a mutable:#lightopen System.Collections.Generic let mutable x = 1let d = new Dictionary<string,int>()d.Add("foo", 42)if d.TryGetValue("foo", &x) then printfn "x = %d" x

    Like

  2. I think if you don’t curry the arguments member public x.SetPropertyValueFS(propertyName, oldVal, newVal) =then it will work. When they’re curried, this effectively makes the .NET method take one argument and return a lambda or the rest of the arguments, and that lambda is capturing a call to a protected method. For reasons that are still unclear to me, that’s not allowed. But you can work around it simply by making the method take parameters "(like, this)" rather than "like this".

    Like

  3. Hi Brian,Thanks for the info with the &var style parameter passing – haven’t seen than anywhere before! I tried it in a simple test and it works, but I’m still having problems in my precise use case – with a member like this: member public x.Age with get() = x.age and set(v) = x.SetPropertyValueFS("Age", &x.age, v) |> ignoreI’m getting a weird error: "Error 3 Type constraint mismatch. The type ‘b byref is not compatibile with type ‘a ref. The type ”b byref’ is not compatible with the type ”a ref’."For the second problem though – yes, I can see where that problem comes from with curried parameter lists (if you accept that capturing the protected method isn’t allowed). Your suggestion solves my problem, so thanks for that! I still don’t understand what makes that particular call so problematic, since the same thing works in simple tests I did…

    Like

  4. Okay, I think I solved this latest problem. I changed the helper function to this signature: member public x.SetPropertyValueFS(propertyName, (oldVal: ‘a byref), (newVal: ‘a)) = That makes it compile correctly – haven’t tested functionality yet. And the "byref" keyword is another thing I haven’t found documented anywhere… I just guessed from the error message that there could be such a keyword.

    Like

  5. Brian, if you’re reading these comments – do you have any thoughts on the original "resolve overload conflict problem"? I’m not getting any feedback on hubFS, should I report this somewhere else? What’s the right place to go for that?

    Like

  6. Sorry, I haven’t been able to log into the hub lately to reply.In your overload example, you have a setter with "set(v)", but "v" is unconstrained. If you constrain it to be "int" (rather than ‘a), then it will work: "set(v:int)". That should explain why it’s happening, too.This is a case where hovering over the variable name with the mouse in Visual Studio to see the type helps a lot.

    Like

  7. Thanks, that might be a good idea – can’t try right now – since I’ve already found another workaround that involved putting int(v) to the function instead of v on its own. For certain other reasons (I want to do CodeRush templates for this, and there are certain type translation algorithm not in place yet for F#) I couldn’t use that workaround. Having the type constraint in the place you’re suggesting will probably solve the problem for me.I hope you guys are taking these things up as bugs though – so far I haven’t heard anybody saying "it works exactly as expected and here’s why"!Thanks for all your help, Brian – sounds like you’ve really dug in by now – in the F# session at the MVP summit you came over just a little bit nervous and I understood it was due to a certain lack of experience at that point 😉

    Like

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s