-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IOType can supply default state methods by using valueType
#213
Comments
Here is the relevant part of that comment: If we automatically leverage ObjectIO = new IOType( 'ObjectIO', {
isValidValue: () => true,
supertype: null,
documentation: 'The root of the IO Type hierarchy',
toStateObject: function( coreObject ) {
return coreObject.toStateObject ? coreObject.toStateObject() : coreObject;
},
fromStateObject: function( stateObject ) {
return this.valueType && this.valueType.fromStateObject ? this.valueType.fromStateObject( stateObject ) : stateObject;
},
stateToArgsForConstructor: function( stateObject ) {
return this.valueType && this.valueType.stateToArgsForConstructor ? this.valueType.stateToArgsForConstructor( stateObject ) : [];
},
applyState: function( coreObject, stateObject ) {
coreObject.applyState ? coreObject.applyState( stateObject ) : null;
}
} ); Then 7 IO Type declarations in Natural Selection change from looking like this: Wolf.WolfIO = new IOType( 'WolfIO', {
valueType: Wolf,
toStateObject: wolf => wolf.toStateObject(),
stateToArgsForConstructor: Wolf.stateToArgsForConstructor,
applyState: ( wolf, stateObject ) => wolf.applyState( stateObject )
} ); to looking like this: Wolf.WolfIO = new IOType( 'WolfIO', {
valueType: Wolf
} ); |
Why does it make sense to take some things from the core type, such as |
Note the philosophy for this issue was introduced in #188, may be good to skim that issue before closing this one, to make sure our bases are covered. |
@zepumph and I discussed these APIs // Reasonable
Bunny.BunnyIO = new IOType( 'BunnyIO', {
valueType: Bunny,
fromCoreType: true
} );
// Possibly better?
Bunny.BunnyIO = IOType.fromCoreType( 'BunnyIO', Bunny ); |
It may be more seamless to move static methods to the core types if we support |
@pixelzoom can you please review the proposal in #213 (comment) ? Does it seem too tricky/magical to you? Or an efficient way to eliminate unnecessary code? |
Can you explain what an IO Type does other than serialization and type verification? If the IO Type is delegating to the Core Type, and the IO Type doesn't have other responsibilities, then why is an IO Type necessary? |
In addition to serialization, IO types form a type system for PhET-iO Elements for wrapper interoperability. They describe methods, documentation, type hierarchy & events that can be emitted. |
Could those responsibilities also be folded into the Core Type, or is there value in keeping them separate? |
There are parts that make that difficult or impossible, such as:
That being said, in some cases we could move some things to static elements on the core type. Since we don't support static attributes yet, the syntax could look like: CoreType.phetioDocumentation = 'An element that etc.etc.';
new IOType('CoreTypeIO',{
valueType: CoreType
}) as opposed to the current syntax: new IOType('CoreTypeIO',{
documentation: 'An element that etc.etc.
valueType: CoreType
}) |
OK, thanks for clarifying. So it sounds like doing away with IO Types altogether is not an option. So back to your original question:
If I understand the various options that have been proposed... I like having both the "non-magical" long form, with a factory method for the "magical" short form. For example: // Long-form
Wolf.WolfIO = new IOType( 'WolfIO', {
valueType: Wolf,
toStateObject: wolf => wolf.toStateObject(),
stateToArgsForConstructor: Wolf.stateToArgsForConstructor,
applyState: ( wolf, stateObject ) => wolf.applyState( stateObject )
} );
// Short-form, convenience
Wolf.WolfIO = IOType.fromCoreType( 'WolfIO', Wolf, { ... } ); |
I really like this idea! How about the above commit. Please review. I'm mainly wondering if it is too strict to make sure that options doesn't have all 4 of these things: if ( assert && options ) {
assert && assert( !options.hasOwnProperty( 'valueType' ), 'fromCoreType sets its own valueType' );
assert && assert( !options.hasOwnProperty( 'toStateObject' ), 'fromCoreType sets its own toStateObject' );
assert && assert( !options.hasOwnProperty( 'stateToArgsForConstructor' ), 'fromCoreType sets its own stateToArgsForConstructor' );
assert && assert( !options.hasOwnProperty( 'applyState' ), 'fromCoreType sets its own applyState' );
} |
@zepumph asked:
I think that's perfect for now. If we find a reason that one of those options should be applied, we can change later. Question... I see this in 225 stateToArgsForConstructor: CoreType.stateToArgsForConstructor, What happens in CoreType doesn't have GenePair.GenePairIO = new IOType( 'GenePairIO', {
valueType: GenePair,
toStateObject: genePair => genePair.toStateObject(),
applyState: ( genePair, stateObject ) => genePair.applyState( stateObject )
} ); |
What about this patch? static fromCoreType( ioTypeName, CoreType, options ) {
if ( assert && options ) {
assert && assert( !options.hasOwnProperty( 'valueType' ), 'fromCoreType sets its own valueType' );
assert && assert( !options.hasOwnProperty( 'toStateObject' ), 'fromCoreType sets its own toStateObject' );
assert && assert( !options.hasOwnProperty( 'stateToArgsForConstructor' ), 'fromCoreType sets its own stateToArgsForConstructor' );
assert && assert( !options.hasOwnProperty( 'applyState' ), 'fromCoreType sets its own applyState' );
}
options = merge( {
valueType: CoreType,
toStateObject: coreType => coreType.toStateObject(),
forwardStateToArgesForConstructor: true, // Only needed for dynamic-element deserialization
forwardApplyState: true // Only needed for reference-type deserialization
}, options );
if ( options.forwardApplyState ) {
options.applyState = ( coreType, stateObject ) => coreType.applyState( stateObject );
}
if ( options.forwardStateToArgesForConstructor ) {
options.stateToArgsForConstructor = CoreType.stateToArgsForConstructor;
}
return new IOType( ioTypeName, options );
} ? |
I don't like I also don't understand this line in the patch: forwardApplyState: true // Only needed for reference-type deserialization Is that comment correct? WolfIO uses dynamic element serialization, and it defines options.applyState. And is applyState really optional with With those caveats, and if How about this implementation? static fromCoreType( ioTypeName, CoreType, options ) {
if ( assert && options ) {
assert && assert( !options.hasOwnProperty( 'valueType' ), 'fromCoreType sets its own valueType' );
assert && assert( !options.hasOwnProperty( 'toStateObject' ), 'fromCoreType sets its own toStateObject' );
assert && assert( !options.hasOwnProperty( 'stateToArgsForConstructor' ), 'fromCoreType sets its own stateToArgsForConstructor' );
assert && assert( !options.hasOwnProperty( 'applyState' ), 'fromCoreType sets its own applyState' );
}
assert && assert( CoreType.prototype.toStateObject, 'class CoreType must implement method toStateObject' );
options = merge( {
valueType: CoreType,
toStateObject: coreType => coreType.toStateObject(),
}, options );
if ( CoreType.stateToArgsForConstructor ) {
options.stateToArgsForConstructor = CoreType.stateToArgsForConstructor;
}
if ( CoreType.prototype.applyState ) {
options.applyState = ( coreType, stateObject ) => coreType.applyState( stateObject );
}
return new IOType( ioTypeName, options );
}
} One more nit... |
Sounds reasonable to me! One small issue:
This will not look up the hierarchy. We could recurse though. |
Good point. And this will also not recurse (in case you missed this addition): assert && assert( CoreType.prototype.toStateObject, 'class CoreType must implement method toStateObject' ); |
We can use Inheritance.js as an example of how to recurse for these checks. |
@zepumph if you need test cases for |
I disagree. I added documentation saying that @pixelzoom please review. |
OK. I took it for granted that the Closing. |
This is explained in #211 (comment), but deserves its own issue for investigation.
The text was updated successfully, but these errors were encountered: