Why does the compiler treat the return type of Func<dynamic, int> as strongly typed?
Asked Answered
V

3

7

Why does the following compile? It sure seems like the compiler has enough info to know that the attempted assignment is invalid, since the return type of the Func<> is not dynamic.

Func<dynamic, int> parseLength = whatever => whatever.Length;
dynamic dynamicString = "String with a length";
DateTime wrongType = parseLength(dynamicString);
Vestavestal answered 24/1, 2014 at 18:43 Comment(6)
I guess the compiler goes a little blind when dynamic is involved, but you'd better wait for someone more knowledgeable, perhaps with the initials J.S.Semester
Is there a way to summon Eric Lippert?Camfort
dynamic gets evaluated at run time, so the presence of a dynamic parameter in a Func seems to be causing the Func to be evaluated at run time (even when static analysis could pick up on the fact that it's going to output an int). @JonB - Eric Lippert did write a couple articles (Google "Dynamic contagion") on the subject.Misdirect
Unrelated but I recommend everyone compiles this and then opens the .exe in ILSpy to see the kind of terrible run-time code that gets generated for these 3 lines of code. (Obviously, this approach has its place, it's just expensive.)Instable
possible duplicate of C# DLR, Datatype inference with Dynamic keywordIrrigation
@JonB: If there's something you want brought to my attention you can leave a comment on my blog or "tweet" with @ericlippert on Twitter. Note that no service-level-agreement is expressed or implied.Buttock
B
7

It sure seems like the compiler has enough info to know that the attempted assignment is invalid, since the return type of the Func<> is not dynamic.

Caveat: Of course I no longer speak for the C# design team; these are my opinions on language design.

So what you're saying here is "I've disabled a safety system. Why isn't the disabled safety system detecting when I do something dangerous and stopping me?" Well, if that's what you wanted then maybe you shouldn't have disabled that safety system in the first place.

That said, you are correct; the "disabled" type safety system could actually continue to work here and detect that you're doing something dangerous. In fact there are many situations where a sufficiently clever compiler could make a type deduction about a dynamic subexpression. You've found one of them. Implementing the necessary analysis is a feature request.

So now the relevant question is: which feature of C# would you like to cut in order to give the development team the budget to design, spec, implement, test, debug, ship and maintain forever a feature which statically finds a bug in a program where the developer is explicitly asking for static checking to be turned off? Keep in mind that the cost includes ensuring that no future feature of the language ever interferes with the compiler's ability to make this deduction; some of those costs are taxes that are paid by the design team in the future.

There are a small number of scenarios where an expression containing dynamic is statically analyzed; for example, there are some overload resolution problems involving static methods with dynamic arguments where the compiler can and does figure out that no matter what is provided at runtime, overload resolution is going to fail. But aside from those few cases, the language design team has historically judged that it's simply not good bang for buck to spend its limited budget on scenarios like the one you've identified.

Buttock answered 24/1, 2014 at 19:43 Comment(3)
I understand the bang-for-your-buck argument, and it answers my question. As a follow up then, if adding this feature to the language was free would there be any reason not to?Vestavestal
@mattbob: There are no "free" features, so we're now reasoning from false premises and therefore are unsound. That said: even if the cost was minimal, are there sufficient drawbacks inherent to the feature that make it low value? None come to mind.Buttock
Nothing unsound about curiosity. Which you have now satisfied, thanks.Vestavestal
I
2

Since the input is dynamic, the compiler can't even determine if the result of your function is an int. It will attempt to cast the result to an int at the time the function is executed and fail if that can't be done. Now if you are asking why you would even be allowed to construct such code given the static type constraint has no way of being enforced - remember that dynamic isn't so much a type as a warning flag for "your normal static type rules don't apply here".

Check out the concept of "dynamic contagion" for more insight.

Interject answered 24/1, 2014 at 18:54 Comment(0)
C
2

"The type is a static type, but an object of type dynamic bypasses static type checking."

If any part of your type is dynamic, the compiler must treat the whole thing as dynamic. So:

Func<dynamic, int> parseLength = <some func>  // is dynamic and type cannot be evaluated until runtime.

So the compiler allows this code:

DateTime wrongType = parseLength

This will compile and, as you expect, will produce a runtime error.

Dynamic typing is useful under certain circumstances. My personal experience of such value is accessing unmanaged code in COM components.

It's basically telling the compiler, "Trust me, this is going to work. =)" Or, "These are not the droids you're looking for"

Cute answered 24/1, 2014 at 19:5 Comment(4)
But the compiler doesn't quite treat the whole Func<> as dynamic, as this produces a compile-time error: Func<dynamic, int> parseLength = whatever => "string instead of int";Vestavestal
Ah, that is interesting. I'd go with @EricLippert's answer. There must be something in place that is still performing some static analysis of the code, probably due to the fact that it is a dynamic within a known Type of Func<dynamic,int>.Cute
@mattbob: Would you expect int M(dynamic d) { return "";} to reason "because formal parameter d is dynamic, the value returned need not match the return type" ?Buttock
Sorry if you weren't asking me. This example looks to me like something the compiler could statically understand to be wrong, regardless of the formal parameters. It would even seem like the compiler could know there was a problem if the return was "return someStringFunc(d);". But the cost of implementation argument makes sense.Vestavestal

© 2022 - 2024 — McMap. All rights reserved.