Earlier quoted context omitted.
> Following the general stupidness of the post: they are now unable to do that because a security consultant said they have to enable and can not break RUFF rule ANN401: https://docs.astral.sh/ruff/rules/any-type/ Okay, then your function which is extremely generic and needs to support 25 different use cases needs to have an insane type definition which covers all 25 use cases. This isn't an indictment of the type sy…
> Don't write functions that support hundreds of input data types But by it's nature, duck typing supports an unbounded number of input types and is what Python was built on. You've already decided duck typing is wrong and strict type adherence is correct, which is fine, but that doesn't fit the vast history of Python code, or in fact many of the core Python libraries.
Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
151–160 of 166 posts
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#152Earlier quoted context omitted.
As a Redditor said: > The standard VC business model is to invest in stuff that FAANG will buy from them one day. The standard approach is to invest in stuff that's enough of a threat to FAANG that they'll buy it to kill it, but this seems more like they're gambling on an acqui-hire in the future.
I have never seen a FAANG company buy a pure programming-language based tooling startup.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#153Earlier quoted context omitted.
As the author of that post, I'd like to point out the example was meant to be stupid. The purpose was to show different ideologies and expectations on the same code don't work, such as strict backwards compatibilities, duck typing, and strictly following linting or type hinting rules (due to some arbitrary enforcement). Although re-reading it now I wish I'd spent more than an evening working on it, it's full of issue…
But there was a conceivable way (maybe not in Python) to make a `slow_add` function very generic, yet only be defined over structures where any conceivable `+` operation is defined. You just have to say the type implements Semigroup. Yes, this would work if the arguments are lists, or integers, or strings. And it won't pass the typecheck for arguments that are not Semigroups. It may not work with Python, but only bec…
from dataclasses import dataclass
from typing import Protocol, Self, TypeVar
class Semigroup(Protocol):
def __add__(self, other: Self) -> Self:
...
T = TypeVar("T", bound=Semigroup)
def join_stuff(first: T, *rest: T) -> T:
accum = first
for x in rest:
accum += x
return accum
@dataclass
class C:
x: int
@dataclass
class D:
x: int
def __add__(self, other: Self) -> Self:
return type(self)(self.x + other.x)
@dataclass
class E:
x: int
def __add__(self, other: Self) -> Self:
return type(self)(self.x + other.x)
_: type[Semigroup] = D
_ = E
def doit() -> None:
print(join_stuff(1,2,3))
print(join_stuff((1,), tuple(), (2,)))
print(join_stuff("a", "b", "c"))
print(join_stuff(D(1), D(2)))
print(join_stuff(D(1), 3))
print(D(1) + 3) # caught by mypy
print(D(1) + E(3)) # caught by mypy
print(join_stuff(1,2,"a")) # Not caught by mypy
print(join_stuff(C(1), C(2))) # caught by mypy
doit()
Now, this doesn't quite work to my satisfaction. Mypy lets you
freely mix and match values of incompatible types, and I don't know
how to fix that. Basically, if you directly try to add a D and an
int, mypy will yell at you, but there's no way I've found to insist
that the arguments to join_stuff, in addition to being Semigroups,
are all of the compatible types. It looks like mypy is checking
join_stuff as if Semigroup were a concrete class, so once you're
inside join_stuff, the actual types of the arguments become irrelevant.However, it will correctly tell you that it can't accept arguments that don't define addition at all, and that's better than nothing.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#154Earlier quoted context omitted.
But there was a conceivable way (maybe not in Python) to make a `slow_add` function very generic, yet only be defined over structures where any conceivable `+` operation is defined. You just have to say the type implements Semigroup. Yes, this would work if the arguments are lists, or integers, or strings. And it won't pass the typecheck for arguments that are not Semigroups. It may not work with Python, but only bec…
Challenge accepted. from dataclasses import dataclass from typing import Protocol, Self, TypeVar class Semigroup(Protocol): def __add__(self, other: Self) -> Self: ... T = TypeVar("T", bound=Semigroup) def join_stuff(first: T, *rest: T) -> T: accum = first for x in rest: accum += x return accum @dataclass class C: x: int @dataclass class D: x: int def __add__(self, other: Self) -> Self: return type(self)(self.x + oth…
I think at this point one starts to fight against Python, which wasn't designed with this in mind. But cool nonetheless.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#155Earlier quoted context omitted.
> Gradual typing means that an implicit "any" (unknown type) anywhere in your code base is not an error or even a warning. That depends on the implementation of gradual typing. Elixir implements gradual set-theoretic types where dynamic types are a range of existing types and can be refined for typing violations. Here is a trivial example: def example(x) do {Integer.to_string(x), Atom.to_string(x)} end Since the func…
How is this function definition (or maybe just its parameter x) "untyped"? There is enough information to deduce that the type of parameter x is empty and the type of the function doesn't matter because there is an error. If the body of the function contained only the first or the second call, the verdict would have been that x is respectively an Integer or an Atom and the type of the function is the type of the cont…
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#156Earlier quoted context omitted.
> Gradual typing means that an implicit "any" (unknown type) anywhere in your code base is not an error or even a warning. That depends on the implementation of gradual typing. Elixir implements gradual set-theoretic types where dynamic types are a range of existing types and can be refined for typing violations. Here is a trivial example: def example(x) do {Integer.to_string(x), Atom.to_string(x)} end Since the func…
ty also implements gradual set-theoretic types, and can represent "ranged" dynamic types (as intersections or unions with Any/Unknown). We don't currently refine dynamic type based on all uses, as suggested here, though we've considered something very much like this for invariant generics. In your example, wouldn't `none()` be a type for `x` that satisfies both `Integer.to_string(x)` and `Atom.to_string(x)`? Or do yo…
In our case, we implement a bidirectional system where before applying x to Integer.to_string, we compute the domain of Integer.to_string (which is integer) and pass it up. If x is a dynamic type, then we refine it. So on the first call, x refines to `dynamic & integer`, then we apply it.
The second refinement fails because it becomes none, so we discard it, but it means the application on Atom.to_string will fail anyway. So yes, we check for emptiness and discard none.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#157Earlier quoted context omitted.
beartype is great, but I only find it useful at the edges. Runtime checks aren't needed if you have strict typing throughout the project. On a gradually-typed codebase, you can use beartype (e.g. is_bearable) to ensure the data you're ingesting has the proper type. I usually use it when I'm dealing with JSON types.
Why isn’t it necessary? Do you mean that with edit-time type checking, you can catch all errors, so no need for runtime verification the edit-time type decls match? What about interacting with other libraries?
Of course, that's only if you trust all the types in your code. You still have escape hatches, such as Any and cast, that can break this guarantee. There are lints (from ruff) and pyright options to help with this. Concerning external libraries, I either use libraries that are 100% typed (which is common these days), or write my own type-safe wrappers around the others.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#158Earlier quoted context omitted.
Challenge accepted. from dataclasses import dataclass from typing import Protocol, Self, TypeVar class Semigroup(Protocol): def __add__(self, other: Self) -> Self: ... T = TypeVar("T", bound=Semigroup) def join_stuff(first: T, *rest: T) -> T: accum = first for x in rest: accum += x return accum @dataclass class C: x: int @dataclass class D: x: int def __add__(self, other: Self) -> Self: return type(self)(self.x + oth…
Pretty cool that you got this far though! I think at this point one starts to fight against Python, which wasn't designed with this in mind. But cool nonetheless.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#159Earlier quoted context omitted.
> Following the general stupidness of the post: they are now unable to do that because a security consultant said they have to enable and can not break RUFF rule ANN401: https://docs.astral.sh/ruff/rules/any-type/ Okay, then your function which is extremely generic and needs to support 25 different use cases needs to have an insane type definition which covers all 25 use cases. This isn't an indictment of the type sy…
> Don't write functions that support hundreds of input data types But by it's nature, duck typing supports an unbounded number of input types and is what Python was built on. You've already decided duck typing is wrong and strict type adherence is correct, which is fine, but that doesn't fit the vast history of Python code, or in fact many of the core Python libraries.
Re: Pyrefly vs. Ty: Comparing Python's two new Rust-based type checkers
#160Earlier quoted context omitted.
> Don't write functions that support hundreds of input data types But by it's nature, duck typing supports an unbounded number of input types and is what Python was built on. You've already decided duck typing is wrong and strict type adherence is correct, which is fine, but that doesn't fit the vast history of Python code, or in fact many of the core Python libraries.
> But by it's nature, duck typing supports an unbounded number of input types and is what Python was built on. You're trying to shove a square peg into a round hole. It's not about right or wrong. Either you want your function to operate on any type, attempt to add the two values (or perform any operation which may or may not be supported, i.e. duck typing), and throw an runtime error if it doesn't work--in which cas…
Yeah, that's literally the point of my Reddit post