-
Notifications
You must be signed in to change notification settings - Fork 12
[ RFC ] Do we want to open up using custom TypeSpec<T>'s for the reified API's? #51
Comments
The definition of UserID can be found here. The all the code of the PoC can be found here #50. Why the current behavior is not well-typedThe current behavior of This creates a situation where the invariants of a // @type TypeSpec<dict<string, UserID>>
$with_manual_typespec = TypeSpec\dict(TypeSpec\string(), MyTypeSpec\UserID());
echo '$with_manual_typespec: '.$with_manual_typespec->toString().\PHP_EOL;
// @typechecker-type TypeSpec<dict<string, UserID>>
// @runtime-type TypeSpec<dict<string, int>>
$with_typespec_of = TypeSpec\of<dict<string, UserID>>();
echo '$with_typespec_of: '.$with_typespec_of->toString().\PHP_EOL; // @typechecker-type UserID
// @runtime-type int (not validated)
$user_id = $with_typespec_of->assertType(dict['string' => 1])['string'];
// TypeAssert collapsed UserID to int and my invariance on UserID is silently violated.
echo "Is my \$user_id a UserID?\n";
\var_dump(\is_user_id($user_id as int)); What I would like to addI know that this blows the encapsulation of us using I suggest adding a Migration pathWe could also make the default behavior of I'd personally like for an exception to be thrown, but this will be impossible to migrate to for larger consumers of this library. Composition of resolversIn my PoC, I assume you have one giant resolver, which resolves all the I left this out of my PoC, since it is not important to this RFC and we may just decide to not reveal |
This looks like a valid usecase to me, I agree we should support it somehow. I'm not sure we can commit to a specific API right now -- from what I remember, TypeStructure was meant to be a "temporary" feature of the Hack language and might be replaced by something completely different eventually (although it's been there for a few years now so it's probably not happening any time soon). But if you're OK with a temporary API that might be changed or removed completely in the future, I wouldn't mind adding something like this. Composition of resolvers (like you mentioned) also seems like an important feature, it should probably be supported from the get-go. In fact, the current implementation of Going even further, we might not even need a special "resolver" abstraction, we could just add a static method to function from_type_structure<T>(TypeStructure<T> $ts): TypeSpec<T> {
foreach (Config::TYPE_SPEC_CLASSES as $class) {
$spec = $class::fromTypeStructure($ts);
if ($spec is nonnull) {
return $spec;
}
}
throw new UnsupportedTypeException(...);
} |
I thought the big switch was made to make sure you were exhausting all the types, but then I saw that it has a default... |
Sorry that was a bad example, of course it can't be a constant, but probably some function that gets all the relevant lists of classes (built-in, custom, ...) and returns them all in the correct order. |
With the current capabilities of TypeAssert, we can derive from
Facebook\TypeSpec\TypeSpec<T>
to implement validation for customnewtype
s. These specs compose just like the built-in specs.__Private\from_type_structure<T>()
(and its friends likeTypeSpec\of<T>()
) will however not know how to find our custom TypeSpecs. This means that you need to manually build the complex TypeSpec object via TypeSpec\xxx() calls in practice.Do we want to offer the capability to supply your own TypeSpecs for your own
newtype
s and have them be recognized byTypeSpec\of<T>()
and friends?This has the major downside of revealing how
TypeSpec\of<T>()
works and will prevent us from changing our implementation away fromTypeStrucuture<T>
. The fact that we useTypeStrucuture<T>
is an implementation detail of this library.I have created a PR which demonstrates how something like this may work.
I am not suggesting that this is the PR that adds this feature.
I just need to be able to point to some example code.
The text was updated successfully, but these errors were encountered: