New F# developer, long time C# developer. As an exercise in learning F#, I'm working my way through Eric Lippert's series on graph coloring translating from his C# to F#. I'm currently working on part two.
The original C# is in the blog post - here's the F# translation so far - but it doesn't compile:
type BitSet = struct
val bits : int
private new(b) = { bits = b }
static member Empty = BitSet(0)
member this.Contains (item:int) = (this.bits &&& (1<<< item)) <> 0
member this.Add (item:int) = BitSet(this.bits ||| (1 <<< item))
member this.Remove (item:int) = BitSet(this.bits &&& ~~~(1<<<item))
member this.Bits = seq {
for item in 0..31 do
if this.Contains(item) then
yield item
}
end
This produces the very mysterious error "error FS0406: The byref-typed variable 'this' is used in an invalid way. Byrefs cannot be captured by closures or passed to inner functions" from the definition of Bits:seq<int>.
Curiously, changing the keyword "struct" to "class" results in valid code. Coming from a C# perspective, this seems like nonsense, but I'm sure there's a valid reason behind it. The question is - how should I write the Bits function? What's the underlying F# principle that I need to understand for this to make sense?
I think the problem is that the this
reference is created as a reference to the current value of the struct, so that you can modify the struct (if you wanted and the struct was mutable).
This causes problem inside seq { .. }
because this generates code inside another class and so the compiler fails to pass the reference to the "this" instance.
If you assign the this
value an ordinary local variable, then the code works fine:
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
As a matter of style - I would probably use an implicit constructor, which makes the code a bit shorter. I also prefer the Struct
attribute over the explicit struct .. end
syntax, but both of these are just a matter of style (and I'm sure others will have different preferences). You might find it useful just as an alternative or for comparison:
[<Struct>]
type BitSet private (bits:int) =
static member Empty = BitSet(0)
member this.Contains (item:int) = (bits &&& (1<<< item)) <> 0
member this.Add (item:int) = BitSet(bits ||| (1 <<< item))
member this.Remove (item:int) = BitSet(bits &&& ~~~(1<<<item))
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With