Naming and resolution of generic types (complete!)

Hi all - I'm cross-posting this to the IP list as the subject seems to be an open issue there too. I'm working on generics support for Python for .NET, and there a couple of thorny issues that could use some discussion regarding naming and resolution of generic types. In C# you can explicitly name a generic type, a la Dictionary<,>. That syntax won't work in Python, of course. Looking at IP, it seems to allow the apparent Python name to be the unmangled IL name so you can naturally use the name. The problem is that the mangled name is illegal as a python name, and the unmangled name isn't guaranteed to be unique - you can potentially have any number of generic types as well as a non-generic type with the same base name in the same namespace: namespace Spam { public class SpamCan {} public class SpamCan<T> {} public class SpamCan<T, V> {} ... } I imagine that maybe IP has enough information available at compile-time to do the right thing for some common usage (binding and instantiating the types), but the overloaded name can still be ambiguous. A real-life example of this is System.IComparable - in IP, it doesn't seem possible to get to the non-generic version of IComparable anymore (or at least it was not obvious to me how to do it):
import System System.IComparable <type 'IComparable`1'>
It seems like we need to figure out some acceptable way of spelling generic type names explicitly in Python (an equivalent to IComparable<> in C#) that works within the existing syntax. There don't appear to be a lot of great options :( It has to be a valid Python name to work in an import statement, so that rules out strange operator shenanigans akin to the [] hack used for generic type binding. One would be to mimic the existing IL mangling in some way, a la:
From System import IComparable # the non-generic type From System import IComparable_1 # the 1-param generic
# or from System import IComparable_T from System.Collections.Generic import Dictionary_TT These are all pretty gross, and still don't totally prevent hiding of legit non-generic classes with those names (though it makes it less likely that names will be hidden than the current approach). I suppose another approach would be to continue to have only one type end up with the simple name, but provide a way to disambiguate it after the fact:
import System System.IComparable <type 'IComparable`1'>
# abandon all hope, ye who enter here... NonGenericIComparable = System.IComparable<<0 OneParamGenericIComparable = System.IComparable<<1 TwoParamVersionIfThereWasOne = System.IComparable<<2
That feels to me like it violates Python Zen in several ways, though. Thoughts? -Brian

My vote is to keep Python pure, i.e. import SomeGeneric_TT or foo = TwoParamGeneric<<2 are simply un-Pythonic. It is amusing that the .NET framework has incorporated a feature --Generics-- that make it more useful, and indeed more like Python, and the Python community is now trying to emulate a solution that has already been solved in Python! OK, I'm generalizing and I recognize that there are exceptions; but by and large, Generics give .NET programmers the flexibility that Python users have always had. The vast majority of programming objectives can be met using plain old Python. Really. A problem arises when there is a .NET system call or 3rd party library that expects Generics in its interface. In those cases, why not just coerce the native Python type and throw an exception if the programmer did something stupid?
import generic_interface_lib int_list = [1, 2, 3, "Hey! A string!", 5, 6] result = generic_interface_lib.Plot(int_list) Traceback (most recent call last): File "<stdin>", line 1, in ? Do_Not_Be_A_Bozo Exception: function requires a list of numbers.
Yes, I do know the answer to the previous question, but this seems to be a more Pythonic solution. Brian's question makes me wonder if Python can be all things to all programmers, and my thinking is: no, it can't. Trying to make it so will just pollute the language. --Thane -----Original Message----- From: pythondotnet-bounces@python.org [mailto:pythondotnet-bounces@python.org] On Behalf Of Brian Lloyd Sent: Friday, March 31, 2006 3:43 PM To: pythondotnet@python.org Cc: users@lists.ironpython.com Subject: [Python.NET] Naming and resolution of generic types (complete!) Hi all - I'm cross-posting this to the IP list as the subject seems to be an open issue there too. I'm working on generics support for Python for .NET, and there a couple of thorny issues that could use some discussion regarding naming and resolution of generic types. In C# you can explicitly name a generic type, a la Dictionary<,>. That syntax won't work in Python, of course. Looking at IP, it seems to allow the apparent Python name to be the unmangled IL name so you can naturally use the name. The problem is that the mangled name is illegal as a python name, and the unmangled name isn't guaranteed to be unique - you can potentially have any number of generic types as well as a non-generic type with the same base name in the same namespace: namespace Spam { public class SpamCan {} public class SpamCan<T> {} public class SpamCan<T, V> {} ... } I imagine that maybe IP has enough information available at compile-time to do the right thing for some common usage (binding and instantiating the types), but the overloaded name can still be ambiguous. A real-life example of this is System.IComparable - in IP, it doesn't seem possible to get to the non-generic version of IComparable anymore (or at least it was not obvious to me how to do it):
import System System.IComparable <type 'IComparable`1'>
It seems like we need to figure out some acceptable way of spelling generic type names explicitly in Python (an equivalent to IComparable<> in C#) that works within the existing syntax. There don't appear to be a lot of great options :( It has to be a valid Python name to work in an import statement, so that rules out strange operator shenanigans akin to the [] hack used for generic type binding. One would be to mimic the existing IL mangling in some way, a la:
From System import IComparable # the non-generic type From System import IComparable_1 # the 1-param generic
# or from System import IComparable_T from System.Collections.Generic import Dictionary_TT These are all pretty gross, and still don't totally prevent hiding of legit non-generic classes with those names (though it makes it less likely that names will be hidden than the current approach). I suppose another approach would be to continue to have only one type end up with the simple name, but provide a way to disambiguate it after the fact:
import System System.IComparable <type 'IComparable`1'>
# abandon all hope, ye who enter here... NonGenericIComparable = System.IComparable<<0 OneParamGenericIComparable = System.IComparable<<1 TwoParamVersionIfThereWasOne = System.IComparable<<2
That feels to me like it violates Python Zen in several ways, though. Thoughts? -Brian _________________________________________________ Python.NET mailing list - PythonDotNet@python.org http://mail.python.org/mailman/listinfo/pythondotnet

Well, I'd argue that its not about trying to be all things to all programmers - its about finding the most reasonable way to handle one of the (relatively few) ways that the meta-models of the CLR and Python don't match up well. In a way, this is not unlike the situation with methods. The fact that Python doesn't have a concept of overloaded methods causes potential ambiguity on the Python side, but luckily there is a relatively elegant solution in that most of the time the runtime can do the Right Thing on its own, and you can hint it with the [] syntax if you need to. I'd like to find a similarly nice solution to this problem, but there are more constraints here in that types are routinely named in import statements. That makes a solution much harder in that you only have a Python name to work with and not many sneaky tricks at your disposal :^/ The more I think about it, I think the best solution might have to be that given the following statement: from System.Bunnies import Bunny then the name 'Bunny' will be bound to something that is one of: - a (reflected) non-generic type (if there exists a non-generic type of that name and no generic type definitions with the same base name). This type can be instantiated with the standard () syntax. - a (reflected) generic type definition (if there exists a generic type definition with the given base name, and no non-generic type with that name). This generic type definition can be bound into a closed generic type using the [] syntax. Trying to instantiate a generic type def using () raises a TypeError. - an object that represents more than one reflected type (if there exists both a generic and a non-generic type with the same base name, or more than one generic type with the same base name). Calling this object with () would create an instance if there exists a non-generic type, else raise a TypeError. Using the [] syntax would produce a closed type. There could be some sort of syntax you could use to obtain a reference to a specific (but unbound and uninstantiated) encapsulated type. This seems like it retains the most simplicity for common uses, and moves most of the ambiguity into less common cases (mostly having to do with a 'reflection context', from either the CLR or Python perspective). For example, what do you see if you do dir() on this pseudo-type? What would you pass to a System.Reflection method that expected a Type? Those still need to be answered, but I'd argue that for the most part normal users of the type from Python won't have to worry about it if we do the above. explicit-is-better-than-implicit-except-when-its-not'ly, -Brian On 3/31/06, Thane Plummer <thane@magna-capital.com> wrote:
My vote is to keep Python pure, i.e. import SomeGeneric_TT or foo = TwoParamGeneric<<2 are simply un-Pythonic.
It is amusing that the .NET framework has incorporated a feature --Generics-- that make it more useful, and indeed more like Python, and the Python community is now trying to emulate a solution that has already been solved in Python! OK, I'm generalizing and I recognize that there are exceptions; but by and large, Generics give .NET programmers the flexibility that Python users have always had.
The vast majority of programming objectives can be met using plain old Python. Really.
A problem arises when there is a .NET system call or 3rd party library that expects Generics in its interface. In those cases, why not just coerce the native Python type and throw an exception if the programmer did something stupid?
import generic_interface_lib int_list = [1, 2, 3, "Hey! A string!", 5, 6] result = generic_interface_lib.Plot(int_list) Traceback (most recent call last): File "<stdin>", line 1, in ? Do_Not_Be_A_Bozo Exception: function requires a list of numbers.
Yes, I do know the answer to the previous question, but this seems to be a more Pythonic solution. Brian's question makes me wonder if Python can be all things to all programmers, and my thinking is: no, it can't. Trying to make it so will just pollute the language.
--Thane
-----Original Message----- From: pythondotnet-bounces@python.org [mailto:pythondotnet-bounces@python.org] On Behalf Of Brian Lloyd Sent: Friday, March 31, 2006 3:43 PM To: pythondotnet@python.org Cc: users@lists.ironpython.com Subject: [Python.NET] Naming and resolution of generic types (complete!)
Hi all - I'm cross-posting this to the IP list as the subject seems to be an open issue there too.
I'm working on generics support for Python for .NET, and there a couple of thorny issues that could use some discussion regarding naming and resolution of generic types.
In C# you can explicitly name a generic type, a la Dictionary<,>. That syntax won't work in Python, of course. Looking at IP, it seems to allow the apparent Python name to be the unmangled IL name so you can naturally use the name.
The problem is that the mangled name is illegal as a python name, and the unmangled name isn't guaranteed to be unique - you can potentially have any number of generic types as well as a non-generic type with the same base name in the same namespace:
namespace Spam {
public class SpamCan {} public class SpamCan<T> {} public class SpamCan<T, V> {} ... }
I imagine that maybe IP has enough information available at compile-time to do the right thing for some common usage (binding and instantiating the types), but the overloaded name can still be ambiguous. A real-life example of this is System.IComparable - in IP, it doesn't seem possible to get to the non-generic version of IComparable anymore (or at least it was not obvious to me how to do it):
import System System.IComparable <type 'IComparable`1'>
It seems like we need to figure out some acceptable way of spelling generic type names explicitly in Python (an equivalent to IComparable<> in C#) that works within the existing syntax.
There don't appear to be a lot of great options :( It has to be a valid Python name to work in an import statement, so that rules out strange operator shenanigans akin to the [] hack used for generic type binding.
One would be to mimic the existing IL mangling in some way, a la:
From System import IComparable # the non-generic type From System import IComparable_1 # the 1-param generic
# or from System import IComparable_T from System.Collections.Generic import Dictionary_TT
These are all pretty gross, and still don't totally prevent hiding of legit non-generic classes with those names (though it makes it less likely that names will be hidden than the current approach).
I suppose another approach would be to continue to have only one type end up with the simple name, but provide a way to disambiguate it after the fact:
import System System.IComparable <type 'IComparable`1'>
# abandon all hope, ye who enter here... NonGenericIComparable = System.IComparable<<0 OneParamGenericIComparable = System.IComparable<<1 TwoParamVersionIfThereWasOne = System.IComparable<<2
That feels to me like it violates Python Zen in several ways, though.
Thoughts?
-Brian
_________________________________________________ Python.NET mailing list - PythonDotNet@python.org http://mail.python.org/mailman/listinfo/pythondotnet
_________________________________________________ Python.NET mailing list - PythonDotNet@python.org http://mail.python.org/mailman/listinfo/pythondotnet

On 3/31/06, Brian Lloyd <Brian.Lloyd@revolution.com> wrote:
In C# you can explicitly name a generic type, a la Dictionary<,>. That syntax won't work in Python, of course. Looking at IP, it seems to allow the apparent Python name to be the unmangled IL name so you can naturally use the name.
The problem is that the mangled name is illegal as a python name, and the unmangled name isn't guaranteed to be unique - you can potentially have any number of generic types as well as a non-generic type with the same base name in the same namespace:
namespace Spam {
public class SpamCan {} public class SpamCan<T> {} public class SpamCan<T, V> {} ... }
I imagine that maybe IP has enough information available at compile-time to do the right thing for some common usage (binding and instantiating the types), but the overloaded name can still be ambiguous. A real-life example of this is System.IComparable - in IP, it doesn't seem possible to get to the non-generic version of IComparable anymore (or at least it was not obvious to me how to do it):
import System System.IComparable <type 'IComparable`1'>
It seems like we need to figure out some acceptable way of spelling generic type names explicitly in Python (an equivalent to IComparable<> in C#) that works within the existing syntax.
There don't appear to be a lot of great options :( It has to be a valid Python name to work in an import statement, so that rules out strange operator shenanigans akin to the [] hack used for generic type binding.
One would be to mimic the existing IL mangling in some way, a la:
From System import IComparable # the non-generic type From System import IComparable_1 # the 1-param generic
# or from System import IComparable_T from System.Collections.Generic import Dictionary_TT
These are all pretty gross, and still don't totally prevent hiding of legit non-generic classes with those names (though it makes it less likely that names will be hidden than the current approach).
I suppose another approach would be to continue to have only one type end up with the simple name, but provide a way to disambiguate it after the fact:
import System System.IComparable <type 'IComparable`1'>
# abandon all hope, ye who enter here... NonGenericIComparable = System.IComparable<<0 OneParamGenericIComparable = System.IComparable<<1 TwoParamVersionIfThereWasOne = System.IComparable<<2
That feels to me like it violates Python Zen in several ways, though.
Okay, I took some time to think about this issue. And here my thoughts: Generic type could be mapped to some Python meta class. If user wants to create some concrete instance he writes Dictionary( int ) or Dictionary( MyClass ) This will create a "template instantiation", sorry for using C++, of class Dictionary. DictionaryInt = Dictionary( int ) Now what to do if user already have "template instantiation" in his code? How to name it? I don't have good answer. You can not provide some mangler that will satisfy all users. May be you should provide some hooks, in order to allow users to write their own mangler.
Thoughts?
-Brian
-- Roman Yakovenko C++ Python language binding http://www.language-binding.net/

I apologize for the extreme delay on this reply... This mail got stuck in our mailing list somewhere and didn't come out until today. The way we've chosen to solve this is to go w/ the single type that allows you to disambiguate after the fact. If you have System.IComparable (another good example is System.Nullable which has both generic & non-generic versions) we build one 'type' that allows you to disambiguate using __getitem__. This is the same way you need to get a generic instantiation (e.g. if there were no IComparable conflicts you'd need to do IComparable[int] to get the specific instantiation) so it fits in nicely. Otherwise the type is the non-generic version. It would seem our str/repr of this type is pretty bad right now (showing you only the generic version) - we should make that more helpful (e.g. something like <type IComparable, IComparable[T]> maybe ?). I guess the other remaining issue is how to get access to the generic type that isn't bound to a specific instantiation - currently this isn't much of a problem w/ IronPython as there's not much you can actually do w/ the open generic type, but we should probably solve it long term. I've opened 2 bugs - the first one (str/repr of the type) should be easy to fix, but some thought probably needs to go into the open generic type issue. We could make some __ __ method to do this, allow indexing by None, or something else - neither of those sounds particularly wonderful. Do you want to help develop Dynamic languages on CLR? (http://members.microsoft.com/careers/search/details.aspx?JobID=6D4754DE-11F0...) -----Original Message----- From: users-bounces@lists.ironpython.com [mailto:users-bounces@lists.ironpython.com] On Behalf Of Brian Lloyd Sent: Friday, March 31, 2006 12:43 PM To: pythondotnet@python.org Cc: users@lists.ironpython.com Subject: [IronPython] Naming and resolution of generic types (complete!) Hi all - I'm cross-posting this to the IP list as the subject seems to be an open issue there too. I'm working on generics support for Python for .NET, and there a couple of thorny issues that could use some discussion regarding naming and resolution of generic types. In C# you can explicitly name a generic type, a la Dictionary<,>. That syntax won't work in Python, of course. Looking at IP, it seems to allow the apparent Python name to be the unmangled IL name so you can naturally use the name. The problem is that the mangled name is illegal as a python name, and the unmangled name isn't guaranteed to be unique - you can potentially have any number of generic types as well as a non-generic type with the same base name in the same namespace: namespace Spam { public class SpamCan {} public class SpamCan<T> {} public class SpamCan<T, V> {} ... } I imagine that maybe IP has enough information available at compile-time to do the right thing for some common usage (binding and instantiating the types), but the overloaded name can still be ambiguous. A real-life example of this is System.IComparable - in IP, it doesn't seem possible to get to the non-generic version of IComparable anymore (or at least it was not obvious to me how to do it):
import System System.IComparable <type 'IComparable`1'>
It seems like we need to figure out some acceptable way of spelling generic type names explicitly in Python (an equivalent to IComparable<> in C#) that works within the existing syntax. There don't appear to be a lot of great options :( It has to be a valid Python name to work in an import statement, so that rules out strange operator shenanigans akin to the [] hack used for generic type binding. One would be to mimic the existing IL mangling in some way, a la:
From System import IComparable # the non-generic type From System import IComparable_1 # the 1-param generic
# or from System import IComparable_T from System.Collections.Generic import Dictionary_TT These are all pretty gross, and still don't totally prevent hiding of legit non-generic classes with those names (though it makes it less likely that names will be hidden than the current approach). I suppose another approach would be to continue to have only one type end up with the simple name, but provide a way to disambiguate it after the fact:
import System System.IComparable <type 'IComparable`1'>
# abandon all hope, ye who enter here... NonGenericIComparable = System.IComparable<<0 OneParamGenericIComparable = System.IComparable<<1 TwoParamVersionIfThereWasOne = System.IComparable<<2
That feels to me like it violates Python Zen in several ways, though. Thoughts? -Brian _______________________________________________ users mailing list users@lists.ironpython.com http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
participants (5)
-
Brian Lloyd
-
Brian Lloyd
-
Dino Viehland
-
Roman Yakovenko
-
Thane Plummer