Closed greyepoxy closed 6 years ago
Hey there,
thank you for your in-depth issue description, really appreciate this! š
Duality serialization and cloning do in fact treat immutable data types a bit like an occasional special case, and it shows in some of their designs. It should, however, be possible to deal with immutable data types properly:
CloneBehavior
solution is a really good solution, as it's exactly how you would handle with immutable collections yourself.That said, default serialization should be able to deal with most immutable types by simply ignoring their immutability and handling their private data fields directly - if that's not the case, we should take a look why and how they're failing in order to see if there's something that can be done on the system level too. Can you provide some information on how / why exactly this failed?
Yes, you are totally right the default serialization works fine. Sorry tried to mention this in the original context but suspect I wrote to many words (kind of a bad habit... š¢). Serialized content does look a little crazy though since FSharp's Map type is a tree internally.
So safe to say what I am doing is "writing a custom serializer in FSharp for an immutable data type (just Map at the moment)."
As you suggested I switched to using the ConstructObject
and WriteConstructorData
methods and it totally works. š
I don't believe I am going to have any circular child <-> parent references in these types so am not super worried about the back referencing.
If you think it is worth it (and a good idea?) can look into if there is some way to make immutable types less of a special case in the serialization code. My naive guess is that the re-assigning the RealObject
prop to obj after the ReadData call should work (probably have to re-register them?, can registration move farther down the function?).
If not that is fine happy to have a workaround, thanks for your time!
Ahh it just occurred to me that probably cannot move the registration call because this is how you get circular references to work... hmm well I think I answered my own question then, doesn't look like there is an straight forward fix to this (using return ref's or parameter ref's might still work but not really sure).
At least would it be helpful if I updated the ISerializeSurrogate
/ISerializeExplicit
summary comments to document this (that immutable objects need to be read/written as part of the ConstructObject and WriteConstructorData methods)?
Yes, you are totally right the default serialization works fine. Sorry tried to mention this in the original context but suspect I wrote to many words (kind of a bad habit... š¢). Serialized content does look a little crazy though since FSharp's Map type is a tree internally.
Ah, got it. I can imagine, default behavior tends to not hit the most optimal way to represent data for collection types. We've had something similar until recently with HashSet<T>
, until Barsonax contributed a surrogate for that.
Ahh it just occurred to me that probably cannot move the registration call because this is how you get circular references to work... hmm well I think I answered my own question then, doesn't look like there is an straight forward fix to this (using return ref's or parameter ref's might still work but not really sure).
Yep, that's pretty much it.
If not that is fine happy to have a workaround, thanks for your time!
Great, glad to hear. Will close this issue then, let us know if something else comes up. And thanks for the thorough description!
At least would it be helpful if I updated the
ISerializeSurrogate
/ISerializeExplicit
summary comments to document this (that immutable objects need to be read/written as part of the ConstructObject and WriteConstructorData methods)?
Sure, always glad to see a new PR, go for it š
Summary
ISerializeSurrogate's implemented in fsharp for immutable types do not deserialize correctly because csharp code assumes it has captured the reference to the real object which fsharp cannot edit directly
So for some context, I have been working on a game in fsharp and in the process of trying to figure out why native fsharp types do not always seem to work correctly to hold component data I attempted to write an ISerializeSurrogate for some of the fsharp collection types. Pretty much the only one that consistently works is the Array type which makes since because it is actually a
System.Array
while the rest are all part ofFSharp.Core
. As I investigated this I realized that serialization actually seems to be working fine but the issue was actually the cloning behavior and I was able to successfully fix my problems by adding a[<CloneBehavior(CloneBehavior.Reference)>]
attribute to the field containing the fsharp collection (a Map in my case). Yay!Because of all of this investigation I did notice that implementing a
ISerializeSurrogate
(or the helper classSerializeSurrogate<T>
) for an immutable type never works. Looks like the core of the problem is that the serialization code assigns to the surrogate's RealObject parameter and assumes that the Surrogate will just edit the given object. For the fsharp core types though there are no mutable methods to use and I was unfortunately unable to figure out a way to modify the object using its reference.How to reproduce
ISerializeSurrogate
. Here is the one I wrote for map, apologies for all the code was just less work to show what I have...ReadData
function this linethis.RealObject <- results
and will see thatresults
does contain the correct mapSome fix ideas
RealObject
prop after the ReadData call. Not sure if this will actually work or not...ref
returns but looks like while f# can read them from c#, the reverse is not possible (bottom of that section). If everything was boxable as an object might be able to provide a 'getObjectRef' function to the plugin with a ref return.Not sure if you think its super important to mess with this right now. In the short term I probably will be the only one who benefits... Would be happy to investigate a fix if we come up with an idea we want to pursue
Thanks!