question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature Request] Contravariant generics (if feasible, but maybe infeasible)

See original GitHub issue

So there I was, happily humming a tune while beartype slays the typing demons of type-tetris’ past… and, suddenly:

import beartype.door.TypeHint as th
import beartype.typing as bt

E_ID = bt.Union[int,str]  # Entity ID's 
DataType = bt.TypeVar('DataType', bound=bt.Optional[bt.Iterable], contravariant=True)
Entity = bt.Tuple[E_ID, DataType]  # So (valid_id, (some_data1, some_data2, ...))

OK, now add some spice…

class Fake(bt.NamedTuple):
    name:str
    kind:str
    velocity:float

MyFakeEntity = Entity[Fake]

and test them all:

(th(Entity).is_bearable(('hi',(2,3,4))),  # this should work
 th(Entity).is_bearable((1.20,(2,3,4))),  # 1.2 is not an E_ID
 th(Entity).is_bearable((0,None)))        # Entity data is optional
 th(MyFakeEntity).is_bearable((0,Fake('n1', 'bob',2.5))),  # is a "fake"
 th(MyFakeEntity).is_bearable((0,('n1', 'bob',2.5)))       # technically not a "fake"
)

(True, False, True, False, False)

wait a minute…?

Obviously without the contravariant=True I get a True, False, as expected. But in my case, entities are just consumers of their datatype, like “tagging protocols”. So if data iterable type B is a subclass of iterable type A, and I can tag arbitrary iterables with EntityA, and only special Fake iterables with EntityB, EntityA should be allowed anywhere EntityB is expected (EntityA is a subtype of EntityB).

All this is to say, I did a quick ctrl+F on that glorious readme of yours, and lo-and-behold, contravariant is mentioned… in the table as “not supported”! 😅 And… I didn’t see any open issues for the feature request. If this isn’t a good place for that, let me know!

Issue Analytics

  • State:open
  • Created 10 months ago
  • Reactions:2
  • Comments:13 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
langfieldcommented, Nov 25, 2022

The phantom type doesn’t do anything magical to the function signature that it is used in, so it won’t make any runtime errors happen for calls to the greet function.

Apologies, yes! Makes sense. I meant with the use of @beartype. My bad entirely.

The slightly less bad thing is that Name now isn’t compatible with isinstance() any longer, so to call greet with a non-literal value, we need to use the underlying phantom type

It will work with beartype.door.is_bearable, though! If only static type checkers could use is_bearable for narrowing, haha!

I do believe that Pydantic would be able to figure out usage of the union though, to be honest I’m not sure if it’d work with beartype, but I think it should?

I do think @leycec supports this currently.

I guess this was sort of off-topic, hope it’s at least tangentially relevant for this discussion 😉

No, no, I think it’s fair to say we’re all pretty deeply interested in this stuff! Thanks for the exposition! 😁

2reactions
langfieldcommented, Nov 23, 2022

As for your question about NamedTuple, my understanding is based from this nice answer which you should absolutely check out!:

When your data structure needs to/can be immutable, hashable, iterable, unpackable, comparable then you can use NamedTuple. If you need something more complicated, for example, a possibility of inheritance for your data structure then use Dataclass.

This is super helpful to know! I bet @dataclass(frozen=True, eq=True) is the most common line of code across all my projects, so I really ought to be using NamedTuples. I can’t wait until @beartype can deeply check them in 0.12.0!

phantom-types, which was pretty much purpose-built for this exact use-case. Note their first example is a str wrap like you have, but the wrap is gone at runtime (type is still str, but isinstance goes to some custom wrapper). @leycec I’m realizing is implementing pretty much exactly what I was poorly approximating in the BearTyped plugin discussion, and I will probably explore what exactly the overlap is in that discussion, since the phantom-types readme even mentions beartype interop eyes

Wow, this looks sweet! I’ll be trying this out soon.

As an aside here, I love to see a Lark person in the wild! I spent a lot of time digging into the parsley library recently, but I almost went the Lark route and read a lot of the documentation sweat_smile. If you’re looking for something like NamedTuples but catered toward ADTs and immutability/memory usage, please please please check out coconut-lang! I love coconut so much. The documentation mentions their custom data keyword

used to create immutable, algebraic data types, including built-in support for destructuring pattern-matching and fmap

Neat stuff! Yes I suppose I have become a Lark person, but under duress. I’d really rather be known as an attoparsec person, lol. I’ve been looking into trying to get a lot of what I’m missing from Haskell in Python. Unfortunately, I think something that compiles to python is just a bit too far away for me. I’ve been looking at using fn.py in my current project, though. I use so many damn lambdas and partial() calls that the underscore syntax and currying is starting to look really desirable.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Issues · beartype/beartype - GitHub
The Future Sound of Beartype: Going Deep on Type-checking ... [Feature Request] Contravariant generics (if feasible, but maybe infeasible).
Read more >
Why is "dynamic" not covariant and contravariant with respect ...
The answer is that when using generics you are abstracted from the data type itself. However, it also implies that generic is generic...
Read more >
Covariance and contravariance (computer science) - Wikipedia
A programming language designer will consider variance when devising typing rules for language features such as arrays, inheritance, and generic datatypes. By ...
Read more >
Generics has moved to "likely accept" phase : r/golang - Reddit
I say this as someone who has strongly advocated for inclusion of generics since Go first went public, and avoided using the language...
Read more >
Covariance and Contravariance in Generics - Microsoft Learn
An invariant generic type parameter is neither covariant nor contravariant. You cannot assign an instance of List<Base> to a variable of type ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found