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

TypeOf x Is SomeType sounds like an exact type check #277

Open
zspitz opened this issue Mar 7, 2018 · 20 comments
Open

TypeOf x Is SomeType sounds like an exact type check #277

zspitz opened this issue Mar 7, 2018 · 20 comments

Comments

@zspitz
Copy link

zspitz commented Mar 7, 2018

Given the following declaration:

Dim s As Object = "abcd"

the following English statement would suggest an exact type check, on the concrete type of s:

If the (concrete) type of s is String then ...

This is also what a straightforward reading of the TypeOf expression would suggest:

'If (the) TypeOf s Is String Then ...
If TypeOf s Is String Then ...

However, TypeOf doesn't work like that (nor should it):

If TypeOf s Is IEnumerable Then Console.WriteLine("True')

prints True, because TypeOf x Is SomeType is actually checking the following, in plain English:

If the (concrete) type of s is type-compatible with SomeType then ...

where type-compatibility in this context means equals SomeType, inherits from SomeType or implements SomeType.

Obviously, programming languages are not natural languages, and we shouldn't expect to express the same meaning in the same way in both. However, I know that this disconnect between the syntax and what it is doing, is something that took me a while to wrap my head around, and I think we can find a more descriptive syntax for type-checking:

  • either some variation of If s Is SomeType Then ... -- we aren't comparing two types, but rather asking if the object supports this type; there is no implication that s couldn't be of other types as well
  • or If AnyTypeOf s Is SomeType Then ... -- expressly implying that s can have multiple types

I prefer the first choice, because it is more directly expresses what we are asking -- if s is a/an SomeType then do something. Under the hood, we are almost certainly extracting the concrete type from the object and checking its assignability with the named type (e.g. Dim t = s.GetType(): If GetType(SomeType).IsAssignableFrom(t) Then...) but I don't see the benefit in expressing this paradigm via the wording of the code. Also, the inverse -- If NoTypeOf s Is IEnumerable Then... is rather clumsy.

There is one issue with using Is / IsNot on its own: since Is also has meaning as reference equality check, how could the compiler differentiate between the two? I'm strongly tempted to suggest that the compiler could infer based on what is on the right-hand side of the Is -- a typename or an identifier. However, it is possible for a name to be both an identifier and a type name in the same scope; so I think this is infeasible as it would introduce ambiguity:

Dim s As String
Dim IEnumerable As IEnumerable = s
If s Is IEnumerable Then ... ' would this be a type check against the IEnumerable type, or a reference equality check against the IEnumerable variable?

Therefore I would suggest one of the following dedicated type check operators:

  • If s IsOfType SomeType / If s IsNotOfType SomeType
  • If s IsOf SomeType / If s IsNotOf SomeType -- this echoes generics, where Of is always followed by a type name

Even if this proposal is accepted, I don't think it possible to invalidate TypeOf. But I think the compiler/editor could rewrite TypeOf to the final form of this proposal; there are a few places where VB.NET already does a similar rewrite.

@zspitz zspitz changed the title TypeOf syntax misleading -- not an exact type check, but assignability type check TypeOf syntax misleading -- not an exact type check, but type compatibility check Mar 8, 2018
@AnthonyDGreen
Copy link
Contributor

I think your example isn't the best for what you're asking; the runtime type of an object is never an interface. No object would ever be IEnumerable; instead they implement the interface. So such a feature would only be useful if the right-hand side were a non-abstract class. Today that can be achieved by asking If obj.GetType() Is GetType(T) Then ...

Could you share the scenario that where you felt TypeOf led you astray? I know of only one scenario where an exact type check is valuable and that's overriding Object.Equals. I'd love to hear about more.

@zspitz
Copy link
Author

zspitz commented Mar 14, 2018

Today that can be achieved by asking If obj.GetType() Is GetType(T) Then ...

Don't you mean If obj.GetType() = GetType(T) Then ...? System.Type has value equality. Or does Is do something special when comparing instances of System.Type?

So such a feature would only be useful if the right-hand side were a non-abstract class.

I don't follow. I am suggesting that TypeOf <x> Is <TypeName> (to an English speaker) sounds like a direct type comparison -- is the runtime type of x exactly equal to TypeName? -- instead of what it actually means -- is the runtime type of x assignment-compatible with TypeName?, which applies when the runtime type equals, inherits, or implements TypeName.

I mean this syntax to be a drop-in replacement for TypeOf <x> Is <TypeName>. Either suggested syntax sounds closer to the actual meaning of TypeOf.

@AnthonyDGreen
Copy link
Contributor

Um, System.Type didn't have an = operator defined until .NET 4.0 iirc so prior to that you had to use Is or .Equals and I'm still in the habit. I believe it's more technically correct probably as you're not guaranteed by the runtime that GetType(Integer) will always return the same instance, even though it always does (at least in the same app-domain). I'm also not sure the rules on the = operator for multi-targeting scenarios or remoting. Is doesn't do anything special here.

I understand what you're suggesting. I'm saying I don't understand how often the distinction between an exact match or an assignment-compatible actually matters outside of one scenario I know of (implementing Object.Equals) and would like to hear more about the scenarios where you'd need this suggestion.

If I'm reading your response right, you're not saying that you have several scenarios where the existing behavior is outright wrong and you need a more correct tool, only that it might be surprising and you'd just like a new syntax which behaves more intuitively for intuition's sake (which is a valid reason to ask, fwiw). I'm I understanding the motivation correctly?

Once again though, I must remind, that if such a new syntax were created which matched your intuition, the type operand could never be any interface or abstract type. Are you ok with that?

@zspitz
Copy link
Author

zspitz commented Mar 14, 2018

it might be surprising and you'd just like a new syntax which behaves more intuitively for intuition's sake

Yes, precisely. Although I wouldn't say behaves more intuitively; this new syntax would have the same behavior as TypeOf. Rather, I would say the behavior is better described in a natural language sense by the wording.

if such a new syntax were created which matched your intuition, the type operand could never be any interface or abstract type

Why? What's the difference between If x IsOf String Then ... and If x IsOf IEnumerable Then ...?

how often the distinction between an exact match or an assignment-compatible actually matters

It doesn't matter to TypeOf, which is used for both; and it wouldn't matter to this new syntax.


you're not guaranteed by the runtime that GetType(Integer) will always return the same instance

Wouldn't Is, checking for reference equality, return False in that case as well? From the docs -- The Is operator determines if two object references refer to the same object.

@zspitz zspitz changed the title TypeOf syntax misleading -- not an exact type check, but type compatibility check More precise type-checking syntax; TypeOf sounds like an exact type check Mar 14, 2018
@zspitz zspitz changed the title More precise type-checking syntax; TypeOf sounds like an exact type check More precise type-checking syntax; TypeOf <x> Is <TypeName> sounds like an exact type check Mar 14, 2018
@bandleader
Copy link

I understand the linguistic opposition to TypeOf x Is T when x's type is actually not T itself but TSub which derives from T.

However, I want to point out that according to OOP principles, you are supposed to necessarily not care whether you a referencing an instance of T or of TSub which derives from T -- in fact, from the perspective of your code, you are actually referencing a living, breathing T -- which will necessarily work with you exactly as you expect a T to -- this is called "widening." The fact that this T actually has additional code hidden inside of it is supposed to be completely invisible to you, unless you are curious and use reflection to check what's really behind it -- which you can do easily using x.GetType = ___.

So for someone thinking in OOP, the language TypeOf x Is T is actually correct.

Nevertheless...

Nevertheless, I agree that it would be more clear had they made it TypeOf x Matches T to be more clear. Are you proposing that we add that today, and still allow Is for backwards compatibility? The problem is that having both keywords will make it look like that Is does an exact type check.

Another problem is that if we do pattern matching, and we allow matching with a type name (as there were plans to do), that means allowing x Is T, which you would again object to linguistically. You would probably anyway want to propose that pattern matching use Matches instead of Is.

@zspitz zspitz changed the title More precise type-checking syntax; TypeOf <x> Is <TypeName> sounds like an exact type check TypeOf <x> Is <TypeName> sounds like an exact type check Mar 14, 2018
@zspitz
Copy link
Author

zspitz commented Mar 15, 2018

@bandleader

So for someone thinking in OOP, the language TypeOf x Is T is actually correct.

Why is extracting the type of something and then checking it against a possibly compatible type (either equal, or a wider type):

Dim x As Object
If TypeOf x Is IEnumerable Then ...
If TypeOf x Is String Then ...

somehow more OOP style than checking whether the something is of a possibly compatible type?

If x Is IEnumerable Then ...
If x Is String Then ...

In either case, checking for a narrowing type is necessitated by the programmer's code logic -- if x is of the subtype T, then do this...

Do you mean we should never allow checking for a narrowing type?

it would be more clear had they made it TypeOf x Matches T

What would be even more clear would be to focus on the x instead of the TypeOf x. With that in mind, x Is T for pattern matching would fit very well.

VB.NET does not have a ValueOf keyword:

Dim n As Integer
If ValueOf n Is 5 Then ...

because the extra step of extract value of n implied by such a keyword is not needed -- just compare against n. Similarly, in order to make a comparison, a type-check shouldn't need the step of extract the type from x implied by TypeOf, when what we want to know is if the object/value referenced by x is an IEnumerable.

allowing x Is T, which you would again object to linguistically

No, I would not. Again, x Is T places the focus squarely on the x -- does x support this type? -- instead of first extracting the type of x.


Consider the following:

If x.GetType() = GetType(IEnumerable) Then ...
If x.GetType() = GetType(String) Then ...

This is what TypeOf x Is IEnumerable appears to be doing, by focusing on the (single -> concrete) type of x and then using Is (which strongly implies equality) for the comparison. In contrast:

If x Is IEnumerable Then ...

asks whether x is an IEnumerable, which doesn't preclude it being of other types, such as List(Of Integer) or HashTable.


The problem is that having both keywords will make it look like that Is does an exact type check.

I think that If TypeOf <x> Is <TypeName> is different enough from If <x> Is TypeName or If <x> IsOf <TypeName> that this wouldn't be a problem.

@zspitz
Copy link
Author

zspitz commented Apr 24, 2018

@bandleader @AnthonyDGreen Do you have any further thoughts on this?

@zspitz
Copy link
Author

zspitz commented May 8, 2018

@KlausLoeffelmann As someone who is interested in making VB.NET more accessible to entry-level users, what are your thoughts on this?

@bandleader
Copy link

bandleader commented May 8, 2018

Do you have any further thoughts on this?

@zspitz I'll gladly summarize my points, as from your response, I think I wasn't completely clear:

  1. You say that TypeOf value Is Control doesn't sound like it will return True if value is of type TextBox. I disagree with this --
    (a) ...not just based on OOP ideas, but simply linguistically: a tiger is a cat, and a cat is an animal. So what's wrong semantically with myTextBox Is Control? It absolutely is a control.
    (b) In any case, as you have agreed, we can't make a breaking change here.
  2. You therefore propose that we use the Is operator (without the TypeOf prefix) for exact type checks. I disagree with this choice of operator for a few reasons:
    (c) It would be very non-intuitive that TypeOf x Is T !== x Is T -- the difference is not visible in the code. You write that the two are "different enough" -- I respectfully very much disagree. Think of someone learning the language, and not following the .NET blog (especially as "someone who is interested in making VB.NET more accessible to entry-level users"). I maintain that it is completely non-intuitable that there is a difference between the two expressions.
    (d) x Is T is a syntax that will be used for pattern matching -- and it will work the same way TypeOf x Is T does today -- so your operator will conflict with that.
  3. If the operator would be called IsExact (or similar), this would solve (c) and (d). However, I am not sure we need a new operator for exact type checks in the first place, for two more reasons:
    (e) when x.GetType() = GetType(String) already works well. Sugar is always nice, but everything comes at a cost.
    (f) Furthermore, if you're working with OOP correctly, I maintain that you should never, ever have a need for an exact type check (i.e. checking that something is a Control but isn't anything that derives from Control. If you're looking to work with a Control you should necessarily not care. I would love to hear your example; I feel we could perhaps guide you to a better method altogether).

@zspitz
Copy link
Author

zspitz commented May 8, 2018

@bandleader Thanks for taking the time to clarify.

The point of divergence is at this statement:

but simply linguistically: a tiger is a cat, and a cat is an animal. So what's wrong semantically with myTextBox Is Control?

There is absolutely nothing wrong with myTextBox Is Control, and in fact I am proposing here that VB.NET use a similar syntax for all type checks -- exact and inexact. It's a precise reflection of the question is this thing referred to by myTextBox a Control? is it a TextBox? The answer to both questions is yes / True, because a TextBox can be of multiple types -- as you say, a tiger is both a cat and an animal.

The issue I am taking with TypeOf myTextBox Is Control is that it appears to be extracting a single concrete type (TypeOf) from the object at myTextBox; and comparing it exactly (as in most other usages of Is) against another type. . The answer to that question, is that the type of (implies only one) this tiger is not the same as the Cat type. I am well aware that this is not how TypeOf x Is y works, but I think it is unclear to someone new to OOP, and the concept that an object can have multiple types.

A better syntax (from the perspective of this proposal) might have been If AnyTypeOf myTextBox Is Control Then, which carries the clear implication that myTextBox can support multiple types, and we want to know if any of those types are exactly equivalent to the Control type. But since we can agree that If myTextBox Is Control Then is a linguistically and semantically appropriate choice, why not use that instead for both exact and inexact type checks?


1b. we can't make a breaking change here. I don't think we can drop the TypeOf variant (for both exact and inexact typechecks) in favor of simply Is (for both exact and inexact typechecks) simply because it would be a breaking change. Perhaps the code editor could automatically remove the TypeOf keyword while editing, like it automatically adds parentheses to Sub/Function calls.

2c-d. I am proposing that Is alone be used for both exact and inexact type checks, exactly as TypeOf x Is y is used today. (I am starting to wonder at my miscommand of the English language, as both you and @AnthonyDGreen seem to have understood the opposite from what I wrote.)

3e-f. I agree with everything you've written here. I reiterate -- I am proposing that Is alone be used for both exact and inexact type checks, exactly as TypeOf x Is y is used today.

@pricerc
Copy link

pricerc commented May 9, 2018

what about something like

If x Implements IEnumerable Then

If s Implements GetType(String) Then

?

@zspitz
Copy link
Author

zspitz commented May 9, 2018

@pricerc TypeOf X Is Y checks for type compatibility -- can a variable of type Y be assigned to from a variable of the type of x? or, GetType(Y).IsAssignableFrom(X.GetType()) -- which is more than just interface implementation. It currently includes three scenarios::

  • X.GetType() is the same type as GetType(Y)
  • X.GetType() inherits from the class type GetType(Y)
  • X.GetType() implements the interface type GetType(Y)

Your syntax is only appropriate to the third possibility. If the type of x is String, then it is incorrect to say that x implements String -- x is a String, or the type of x is equivalent to String.

I don't see any reason why a new type-checking syntax shouldn't be doing exactly the same.

@zspitz
Copy link
Author

zspitz commented May 9, 2018

@bandleader @rskar-git @AnthonyDGreen I've rewritten the initial proposal to clarify that I'm not proposing any change or limitation to type-checking behavior.

@pricerc
Copy link

pricerc commented May 9, 2018

@zspitz
As several discussions in this forum have shown, some comments get a bit 'lost in translation', and I fear mine is one of them.

I know what TypeOf and GetType do, I was just proffering a possible, if not fully-thought-through alternative, which is why I opened my comment with "Something Like".

Your original post referenced IEnumerable as an example a few times, so my suggestion sprang to mind as a simple alternative.

I wasn't completely happy with 'Implements', but I was on site at a client, and didn't have time to think of something better, or to expand my comment.

Looking through the thread again, and thinking about it a bit this time, I think the best idea is your 'IsOf' suggestion. I didn't initially like it, but 'Of' does carry pretty much the same semantics where its arguments can be an interface, abstract class or concrete class.

Like others, I'm not crazy about semantic changes that might catch someone out, especially if it's subtle. I've been burned enough to know that the thing that's going to catch you out is the one you haven't thought of, so I'd be very reluctant to do anything that changes semantics.

There is one possibility I can think of that might not work for your 'Is' suggestion: if you have a variable and type with the same name; e.g. (completely contrived):

' probably a really bad variable name, but hey, that's what rookies do!
dim control as object
...
if textBox1 is control then...

@zspitz
Copy link
Author

zspitz commented May 9, 2018

@pricerc

There is one possibility I can think of that might not work for your 'Is' suggestion: if you have a variable and type with the same name.

I've incorporated this into my original post, and I think this is a real dealbreaker to using Is (as opposed to some other variant such as IsOf).

@zspitz zspitz changed the title TypeOf <x> Is <TypeName> sounds like an exact type check TypeOf x Is SomeType sounds like an exact type check May 9, 2018
@gafter gafter added LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future LDM Review in progress LDM Review is in progress labels Jul 15, 2018
@KathleenDollard KathleenDollard removed the LDM Review in progress LDM Review is in progress label Jul 20, 2018
@zspitz
Copy link
Author

zspitz commented Apr 30, 2019

I would like to add that I have heard a number of times from various people that they avoid the TypeOf <x> Is <y> syntax because it is too complicated and confusing to remember. As I've outlined above, I feel this is because the English-language wording of the operator is at odds with how types and typechecking work in VB.NET and the CLR.

If the syntax were simply odd or meaningless in English -- e.g. Dim, odd placements of Not as in If Not dict.ContainsKey("Key") Then etc. -- this wouldn't justify introducing a new syntax alongside an existing one. But the current syntax suggests an inaccurate meaning.

@KathleenDollard Could you describe the LDT's considerations in not pushing this?

@KathleenDollard
Copy link
Contributor

My dog Io is an instance of a dog and a dog is an animal. All of the following are equally true

(The type of) Io is a dog
(The type of) Io is an animal

I think expanding the surface area is something to do very very carefully. If there were both

TypeOf Io Is Animal
Io is Animal

I have no idea how I would keep straight which is which. We can't change TypeOf behavior, but my intuition would be exactly wrong.

Also, C# uses is for compatible not , and we will think seriously, hesitate, etc. to do things that require different subtle thinking than C#. About half of the people that do VB also do C#.

And the alignment with C# would be even more important in pattern matching, where Is will probably mean what it does today.

Summary

So, personally I don't agree with the premise of the implications of Is. I think the semantics imply exactly what TypeOf..Is does.

More importantly - this change would expand the surface area (adding a second way to do something) and be inconsistent with C# and probably be out of alignment with our future plans.

Of course we will sometimes expand the surface area, but we're looking for big benefits there, like pattern matching. And we may do something explicitly inconsistent with C#, but it would be very high bar to respect the crossover programmer.

Call to action

That said, C# has a better introductory sentence for is than VB's TypeOf in docs.

Everyone! This is super low hanging fruit folks...Take the C# intro sentence, morph it to your VB aesthetics as desired, and update the first sentence in TypeOf

We're OSS, we're a community. Someone want to grab this and find out how easy our docs are to update?

@zspitz
Copy link
Author

zspitz commented Apr 30, 2019

@KathleenDollard Thanks for your quick response.

So, personally I don't agree with the premise of the implications of Is. I think the semantics imply exactly what TypeOf..Is does.

The problem is not in the implication of Is. The problem is in the implication of TypeOf. Consider the following statement:

The address of the Empire State Building is New York State.

This is wrong, because even though both the ESB and its address are in New York State, the statement relates to the single concrete address of the ESB, which is not equivalent to New York State. Both of the following statements are more accurate.

The address of the ESB is in New York State.
The ESB is in New York State.

By analogy, either of the following would be clearer, more understandable, and thus less error-prone syntaxes for checking type-compatibility:

TypeOf Io Matches Dog And TypeOf Io Matches Animal

implying that the concrete type of an instance can match multiple types (concrete Class or abstract Interface/MustInherit); or

Io IsOf Dog And Io IsOf Animal

which doesn't deal with the concrete type at all, but directly with the instance.


If there were both

TypeOf Io Is Animal
Io is Animal

I have no idea how I would keep straight which is which. We can't change TypeOf behavior, but my intuition would be exactly wrong.

To be clear, I am proposing that both variants would have the same exact behavior as TypeOf ... Is ... today -- type compatibility and not only type equivalency; there would be nothing to keep straight, except that the TypeOf keyword would be optional, and would only be needed for backward compatibility.

(I am despairing of my ability to communicate in English, considering the number of times I've had to repeat this point.)

Also, C# uses is for compatible not , and we will think seriously, hesitate, etc. to do things that require different subtle thinking than C#. ... And the alignment with C# would be even more important in pattern matching, where Is will probably mean what it does today.

The behavior of both syntaxes would be exactly the same as the C# is operator.

@KathleenDollard
Copy link
Contributor

My apologies for misreading. Thank you for clarifying.

To be clear, I am proposing that both variants would have the same exact behavior as TypeOf ... Is ... today

That is now clear :)

I think we could look at this again as we look at patterns. If you wold like to put a comment in the pattern matching issue to consider this, that would be fine. When we look at type check/assign this will potentially appear in new contexts, which would be a good time to add it.

I removed the no-plans tag so we can look at it in that context - which will be much later this year after we land the .NET Core 3.0 work.

@KathleenDollard KathleenDollard removed the LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future label Apr 30, 2019
@pricerc
Copy link

pricerc commented Apr 30, 2019

I'm one of those who rarely uses TypeOf - I because I'm more likely to either a) create a generic method, b) create method overloads that accept different data types, or c) some combination of those. But I digress.

Because I use TypeOf so little, I usually get it wrong when I do use it, and often end up using GetType() instead (because getting that working is faster than me finding and reading the manual entry for TypeOf).

But I think I would remember x IsOf SomeType, because it's clear. It maybe would not have been so clear in VB7, but we've gotten used to Of being used in relation to types (especially T), so it resonates with Function(Of T), Inherits List(Of T).

So I can see the merit in Is on its own remaining as a reference equality operator and, as suggested, x IsOf SomeType be an alternative form of TypeOf x Is SomeType.

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

No branches or pull requests

6 participants