On 12/19/2013 10:34 PM, Ævar Arnfjörð Bjarmason wrote:
> As far as speed is concerned we could still have our cake and eat it
> too. We could just provide some default built-in hook that did exactly
> what your initial proposal suggests, or maybe a flag saying that the
> hooks should only be called on classes that ->can($whatever). My point
> was mainly that I think it would be very nice that if we had a
> facility like this it *also* provided the ability to have first-level
> support for passing all objects through the same callback.
So for the record, a conversation that Yves and I had on Jabber the
other day leading up to this was basically (freely paraphrased):
me: "I still like the FREEZE/THAW interface. It's the only sane way to
avoid breaking encapsulation."
Yves: "Well, yeah, I don't care as long as I can have the generic
callback which is more powerful in many situations."
me: "But the thing is, I might give it a shot to implement F/T, but
currently have no interesting in hacking on the other."
Yves: "We can agree to eventually have both and whoever does the work of
either gets to have it."
In other words, the only reason why F/T came first was because I removed
the foot from my mouth first. :)
> Also is the point of adding this to the actual Sereal format rather
> than just having it be something that would be a encoder/decoder
> implementation detail so that you can avoid work when deserializing
> because you'll know which objects you want to check ->can("THAW") on?
I think we all agree that the generic callback interface can be used to
implement F/T semantics as you demonstrated.
The point of having it in the actual format is that the representation
of the object is going to be different "on the wire" than in memory
(even beyond simple flattening). Thus, it makes sense to allow the
encoding side to signal things to the decoding side.
Damian has also said that this actually solves a problem in the Go
implementation wrt. things that the Go introspection doesn't allow him
to do. I would expect other statically typed languages to need such a
thing even more. I'll leave the explanation of how this is important to
his Go port to Damian.
> I'd just like to point out that if that's the case it might be useful
> to either drop that or have a facility in the decoders to call this
> for *all* objects regardless if if they have the tag or not.
>
> Consider the case of serializing Some::Object v0.01 to disk and later
> trying to restore that with Some::Object v2.00 not having known at the
> time that the internal structure changed from an ArrayRef to a HashRef
> or something.
The versioning mismatch is a problem that ALL of these approaches have.
But who is more qualified to anticipate this than the author of the
class whose instance representation changed? Nobody, indeed. So if that
author were to implement FREEZE/THAW hooks, he is in the unique position
to write logic that will make them backwards compatible with previously
serialized v0.01 objects. Attempting to fist-violate encapsulation from
the outside isn't going to pretty for such cases.
Yves had a very, very important point to make about this, and I'll try
to paraphrase it: At B.com, we are in control of most code we use, code
infrastructure classes, application, all. That's not a situation most
users are in. If upstream authors adopt the relatively generic (almost
serializer-agnostic) F/T callbacks in their libraries, then everybody
wins. The most obvious example to us Perlers is of course modules on
that we use CPAN.
> I could see being able to munge such objects when you thaw them when
> you weren't initially expecting to have to munge them would be very
> useful in some cases.
I have no problem with supporting a generic callback at all, so let's
make sure you're not running into open doors here. I just have three
conditions (which are pretty similar to the conditions that Yves
proposed for me adding F/T):
a) I don't have to implement it.
b) "You only pay for it if you use it."
c) It's fairly well thought-through. There's lots of performance
trade-offs vs. more complex interfaces to be made.
The nice thing about a generic callback is that it doesn't require any
changes to the Sereal protocol - certainly not any on top of the one I
am proposing here. That means it can be added at will in the future
whenever somebody volunteers.
Best regards,
Steffen