Skip to content

Allow type parameters to be declared co/contra variant on classes.Β #171

Closed

Description

@mburbea

Currently type parameters can be declared co/contra variant on either an interface or a delegate. I propose adding support for classes.

The rules would remain the same, any in T cannot appear as any method return type nor on gettable properties. Any out T cannot appear as a parameter or on settable properties. My main motivation for this is really around things like Task<T> where I sometimes want to collect Task<Dog>, Task<Cat>, Task<Fish> into a collection of IEnumerable<Task<Pet>> and whenAlll/whenAny and only inspect their pet based result.

It cannot be legal on structs as the assignment would be a copy operation.

Activity

  1. svick commented on Jan 30, 2015

    @svick
    Member

    How would this work with accessibility? For example, could out T type have a non-public field of type T (just like Task<T> does currently)?

    If that were allowed, how would you make sure the following couldn't happen?

    class Task<out T>
    {
        private T value;
    
        public static void SetValue(Task<T> task, T value)
        {
            task.value = value;
        }
    }
    
    …
    
    Task<Pet>.SetValue(new Task<Dog>(), new Cat());

    If it wasn't allowed, then I think that significantly limits the usefulness of the feature. For example, I don't know how would you implement Task<T> under such restrictions.

  2. mburbea commented on Jan 30, 2015

    @mburbea
    Author

    Presumably it would be legal to declare private fields of co/contra variant generics. The static method you described could not be declared as T is an out parameter and therefore can not appear as an input.

  3. thomaslevesque commented on Feb 7, 2015

    @thomaslevesque
    Member

    I'm not sure it would be feasible... there were good reasons to limit variance to interfaces and delegates in C# 4 (although I admit I don't remember exactly what they were). I agree that the Task<T> issue is annoying, but a better solution for this would have been to make Task<T> an interface rather than a class... well, I guess it's too late for that now (or is it?)

  4. mburbea commented on Feb 7, 2015

    @mburbea
    Author

    Yeah if TPL used an ITask<T> this probably wouldn't have been such a large issue. I'd be curious as to what insight let to to limiting variance to just delegates and intefaces.

  5. thomaslevesque commented on Feb 7, 2015

    @thomaslevesque
    Member

    @mburbea see this answer by Eric Lippert: http://stackoverflow.com/a/2734070/98713

  6. iskiselev commented on Feb 18, 2015

    @iskiselev

    @svick and @mburbea , so if I understand correct, Task can be implemented with something like:

    class Task<out T>
    {
        private readonly TaskResultAccessor<T> resultAccessor;
    
        private T Result { return resultAccessor.Value;}
    }
    
    class TaskResultAccessor<T> 
    {
        internal T value;
    
        public static void SetValue(TaskResultAccessor<T> accesor, T value)
        {
            accesor.value = value;
        }
    }
    

    So we split Task and result storage into to classes: immutable covarint Task and non-variant result storage.
    But it gives us not much more than using interfaces for it.

  7. gafter commented on Aug 15, 2016

    @gafter
    Member

    This would be a large undertaking including required CLR support, for marginal gain in language expressiveness. We don't think this is where we want to invest.

  8. voroninp commented on Oct 29, 2024

    @voroninp

    I see recently how people hype the usage of Result/Either for the flow control instead of exceptions. And for these types covariance is definitely a win. Yet nobody asks to reconsider this feature request.

    Either people just ignore the pain, or they never actually used the advertised approach extensively. =)

  9. CyrusNajmabadi commented on Oct 29, 2024

    @CyrusNajmabadi
    Contributor

    It's not that we haven't reconsidered it, it's that we don't know how to provide it.

    How would this actually work? The types people mention as wanting support for don't necessarily even work variantly.

  10. voroninp commented on Oct 29, 2024

    @voroninp

    @CyrusNajmabadi , covariance is mostly useful for monadic types.

    But how does Scala compiler ensure the correct variance for the arguments of the class then?

  11. CyrusNajmabadi commented on Oct 29, 2024

    @CyrusNajmabadi
    Contributor

    At the end of the day, variance is a compile time construct on the jvm. At runtime, nothing exists and you can get runtime exceptions if you violate safety.

  12. voroninp commented on Oct 29, 2024

    @voroninp

    @CyrusNajmabadi So we need the runtime to be able to prove safety. I have no clue how expensive it is for the runtime team to do it. I guess it would be easier if CLR had fair readonly/immutable fields.

  13. CyrusNajmabadi commented on Oct 29, 2024

    @CyrusNajmabadi
    Contributor

    Yup. At the runtime level, variant interfaces and delegates are entirely safe. We'd need the same for classes. It's much more difficult though due to fields.

  14. voroninp commented on Oct 29, 2024

    @voroninp

    @CyrusNajmabadi I'm curious whether JIT will unlock a whole new set of optimisations for strictly immutable fields.;-)

    I guess it could be useful for the new Object Stack allocation feature introduced in .NET 9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions