This is interesting. The `Any` type in Typed Racket includes values that may have higher-order "original" types. For example, it may have originally been a typed function (-> Fixnum Fixnum) or (-> String Boolean), such that if you call it without a contract guarding it, it should be an error. This is because `Any` is a super type of those types.
However, the value returned by `dynamic-require` is different from a normal `Any`. It will never be an unguarded "originally" typed value, so it should be safe to pass it to `cast` without adding a new guard. It might make sense to define a new type `UntypedAny` or maybe `GuardedAny`. There are two differences:
(1) this type is *not* a super type of any other type that might involve higher-order values
(2) it is safe to pass a value of this type into untyped code without an additional guard
Point (2) is what would allow `cast` to work, and point (1) is what would make that safe to do.
With this new `UntypedAny` type, dynamic-require could return the type `UntypedAny` instead of `Any`.
Why this would allow `cast` to work (and why `require/typed` is a good workaround for now)
The difference between `cast` and `require/typed` is this:
- `require/typed` guards value going from Untyped -> Typed
- `cast` guards values going from Typed -> Untyped -> Typed
The `cast` needs the extra Typed -> Untyped boundary to guard "originally" typed higher-order values, such as a (-> Fixnum Fixnum) function annotated as `Any`. However, `require/typed` takes its values from an untyped context to begin with, so it doesn't need to deal with that extra boundary.
The value returned by `dynamic-require` is never one of those typed higher-order values, and if the type system knows that through the `UntypedAny` type, the type system will know it is safe to make `cast` behave like `require/typed`.
It will know it is safe to pass it only through the Untyped -> Typed boundary.