Skip to content
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

Late-bound member access and invocation expressions #137

Closed
AnthonyDGreen opened this issue Jul 12, 2017 · 10 comments
Closed

Late-bound member access and invocation expressions #137

AnthonyDGreen opened this issue Jul 12, 2017 · 10 comments

Comments

@AnthonyDGreen
Copy link
Contributor

This proposal addresses scenario #135 and is an alternative to proposals #136 and #43.

People who request #136 (a Dynamic type in VB) usually do so because they prefer to use Option Strict On but very occasionally need to escape its enforcement for some pragmatic purpose. The advantage of Dynamic is that late-binding is only narrowly permitted but it still has the disadvantage of being opaque to readers. For the Option Strict On crowd transparency is highly prized. Therefore, I have a proposal that I think would leap frog the C# design, addressing the scenario with lower implementation cost and more transparency.

Taking inspiration from the newly added null-conditional member access ?. and null-conditional invocation ?() expressions we added in VB14 I propose a late-bound member access !. and invocation !() expressions. I picked the ! character because VB already has this dictionary-access operator which results in a run-time lookup, this is very similar.

Option Strict On

Dim o As Object = 1
Console.WriteLine(o.ToString("C02")) ' This is an error.
Console.WriteLine(o!.ToString("C02")) ' This is allowed.

Dim arr As Object = {1}
Console.WriteLine(arr(0).ToString()) ' This is an error.
Console.WriteLine(arr!(0).ToString()) ' This is allowed.

Not that unlike ?. this isn't right-associative so to do subsequent late-bound access requires more explicit notation:

Option Strict On

Dim arr As Object = {1}
Console.WriteLine(arr!(0).ToString("C02")) ' This is an error because ToString is still resolved at compile-time.
Console.WriteLine(arr!(0)!.ToString("C02")) ' This is allowed.

This proposal doesn't create any new pseudo-type and makes it easy to see when scanning code that a run-time lookup is occurring.

Should these expressions be restricted to values of type Object?
There's no reason they have to be.

Should these expressions combine with ?., e.g. obj?!.Foo ?
Ugly but probably.

Should these expressions work in a With block?
Yes.

@craigajohnson
Copy link

From my perspective, this is a remarkably observant proposal. At the heart of the matter is to be very explicit and to declare intent directly when performing late-bound access and to carve out those scenarios only as needed. The ability to mix & match is very appealing and I would have no problem with the !. syntax. If anything, the intent would then be even more apparent/obvious/direct. This would significantly clean up the current workarounds including partial class files (ugly) and scads of CallByName+NameOf/other similar wrappers (also ugly but oh well). And it sounds like doing this would alleviate the pains/risks of a full dynamic implementation which in reality is only useful in certain scenarios and may be difficult to justify the community's (I count myself as a pesterer on this) continued pestering to just get it done anyways. +1!

@xieguigang
Copy link

probably have conflicts with System.Single type char:

Dim x!
' currently legal for Single type
x!.CompareTo(9)

@rskar-git
Copy link

Overall, I like this idea too. Cleanly allows late-binding in a Explicit On context.

Should these expressions be restricted to values of type Object?

As @xieguigang pointed out, there's a conflict with the ! for Single variable declarations. Not sure where else this ! for late-bound member access would be meaningful beyond Object.

I suppose if anyone felt the need for late-binding some other type, whether value or reference, they could always type-cast with CObj() first. Not sure why though.

@AnthonyDGreen
Copy link
Contributor Author

@xieguigang there are a couple of solutions to that. 1) decide it's a targeted enough break to accept. 2) quirks it. Basically, on Object expression!.Name will do a late-bound lookup, and on Single expression!.Name will do an early bound lookup for back-compat.

@Bill-McC
Copy link

Bill-McC commented Aug 8, 2017

I would have thought this would be allowed on any type that is not sealed (not NotInheritable). So you couldn't use late bound call on intrinsic value types, or String etc.

Requiring to cast back to Object would seem to defeat much of the purpose and also blurs whether or not the type would in any likelihood support the late bound call.

The late bound member name is known at coding/compile time. The notion that you would have to then cast to Object so as all other member names are late bound is counter productive. This should be allowed on any inheritable type. (expando)

I'd also like a way of error trapping elegantly when the member name doesn't exist. foo?!bar doesn't handle the case when bar does not exist (or wouldn't be expected to). Would !! be too much ?

Ultimately I'd like to be able to do calls such as :
Dim extName As String
extName = mygadget!.ExtensionName

or perhaps
extName = mygadget!!.ExtensionName

or would it be more VB'ish as :
extName = mygadget!Try.ExtensionName

Oh, and whilst exanpo is still in mind, should there be an interface a type can implement so as these late bound calls call on it first when resolving member names. Similar to the dictionary concept, but the member name could be a data, method or a collection etc ?

@Echo-8-ERA
Copy link

Will this change allow dropping the restriction preventing calling extension methods on references typed Object?

@AnthonyDGreen
Copy link
Contributor Author

No. That would require some other mechanism. What kind of extensions on Object are you finding yourself defining or wanting to use?

@Echo-8-ERA
Copy link

Was just curious. I just had happened to hit upon the restriction when I was writing some unit tests for a generic extension method.

@jrmoreno1
Copy link

I don't think this and #43 are mutually exclusive. Both sound like great ideas, but IMO #43 while more verbose better expresses intent. It would allow the caller to define what they expect to be available, and not just what was used.

@AnthonyDGreen
Copy link
Contributor Author

The VB LDM looked at this issue on August 23rd and rejected this proposal. Liked the concept and design but the benefit seemed very narrow. One place this looked particularly promising was for cleanly inspecting JObjects from JSON.NET. Today in C# people sometimes prefer using the dynamic type because it makes getting to a member you want as simple as obj.Property1.Property2 and this would enable that for VB customers in that narrow scenario. However, VB customers can already do that with the dictionary-access ! operator obj!Property1!Property2 and that feature utterly destroyed all remaining value in this proposal. For scoping we decided #117 was a simpler proposal that wouldn't add new concepts to the language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants