Interoperability with native JavaScript
Unbounded dynamic type:
fun jsFun(p: dynamic): dynamic
return
expressions when the return type is dynamic
?Unit
when return type is dynamic
?dynamic
dynamic
are themselves dynamic typestype : ... | "dynamic" ;
“dynamic” is a soft keyword:
<
, it's an identifier::
in a callable reference: dynamic::foo
implies that dynamic
there is a normal identifierInternally, dynamic
is represented as a flexible type Nothing..Any?
, with the following capabilities:
Rules:
dynamic
is assignable to anythingdynamic
dynamic
variable may hold null
dynamic?
is the same as dynamic
, a warning is issued on usages of this syntactic form!!
issue no warnings when called on dynamic
expressionslub(T, dynamic) = dynamic
glb(T, dynamic) = T
dynamic
can‘t be substituted for reified parameters of function/constructor calls (this means that it’s not possible to create an array of dynamic
)is
, !is
, as
and as?
(but not as generic arguments, e.g. x is List<dynamic>
is allowed)dynamic
can't be used as a supertype or upper bound for a type parameterdynamic
is less specific than any other typeWhen there are two function available
fun foo(s: String) fun foo(d: dynamic)
the first one is resolved whenever a matching argument is passed (because dynamic
is less specific than String
), i.e. both calls:
are resolved to the same function foo(String)
. This may seem counter-intuitive in the latter case, but there's no sane way around it.
Calls like foo(1)
are resolved to foo(dynamic)
, because foo(String)
does not fit the arguments.
To force the call of foo(dynamic)
on any expression, one can up-cast the argument to a static type, e.g. foo(dyn as Any)
If a receiver of a call is dynamic, the following resolution rules apply:
Any
unless we implement bounded dynamics, see Appendix below).dyn += foo
) are resolved to plusAssign()
function, not plus
, for generality: this permits calling them on vals (e.g. those holding collection-like objects)dyn.foo()
we do not look for property foo
that has invoke
defined on it (same for other cases like +dyn
etc)foo()
, we first try to match static candidates, and only then dynamic ones.NOTE: we do not even try to resolve extensions declared for static types if the receiver is dynamic. As a workaround, one may use an upcast to a static type: (dyn as Foo).extensionForFoo()
.
Motivation: otherwise, any extension to any type that simply happens to be in scope and match the name and arguments will be bound for a call with a
dynamic
receiver, i.e. there's no way to force a call to be dynamic, and in the case of a*
-import the code may change its semantics just because somebody added some extension in another file.
dynamic
receiver without an upcast.When expected type of a call is dynamic
, it does not automatically provide type arguments for nested calls. Example:
fun foo(d: dynamic) {...} foo(listOf()) // can't determine T for listOf<T>()
Discussion:
dynamic
as a bound for all type variables whose containing type has a dynamic bound, but it‘s hard to be sure it’s worth the while(not to be implemented now)
A bounded dynamic type dynamic B
is represented as (Nothing .. B?)
.
Calls on such receivers are resolved statically against members of B
, and dynamically against non-members of B
(including extensions).
NOTE: this is an issue: some users would expect extensions to be bound statically, but we can't allow it, because otherwise a dynamic call with a name clashing with a name of an extension to
B
is impossible. Options:
B
(i.e. extensions to Any
for dynamic
) statically, this leads to unexpected changes in semantics when a new extension is added in a *-imported package. Then, to make the dynamic calls possible, provide some sort of an intrinsic extension, e.g. dynamic
) that takes a string for a name and a varargs of parameters of type dynamic
. Thus, to call a recv.foo(a, b)
as a dynamic call, we can always say recv.dynamic("foo", a, b)
.foo(a)
instead of a.foo()
. This poses no risk of accidentally changing semantics of some calls from dynamic to staticAssignability rules:
B?
can be passed where dynamic B
is expecteddynamic B
can be passed where any supertype of B
or subtype of B
, but not a type unrelated to B
is expectedUnbounded dynamic
is the same as dynamic Any
.