dazzlefruit's comments

dazzlefruit | 2 years ago | on: You are not late (2014)

What about the App Store? I never used a Symbian phone but I don’t remember a device that would have allowed as much as the iPhone in the way of custom apps.

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

> Python doesn't work well with file names in principle: it wants everything to be Unicode. That works for many, but if you want reliable code... you just have to throw all of that away.

Windows' APIs use UTF-16 and most file name encodings on Linux are UTF-8. How should Python handle this better?

> Also, in case of pathlib, it adds no value on top of os.path of which it is a wrapper.

Completely disagree. os.path is annoying to use. Treating paths as objects with methods and joining them with / makes my life much easier.

> increasing the size and memory footprint of "standard" library

By a ridiculous amount. pathlib is just another pure Python module with a bunch of simple functions and classes. [1]

> complicating dispatch and therefore debugging

You can simply declare and accept `Union[str, os.PathLike]` and convert the paths to whatever you want at the entrypoints, then use that in your own project. Where is the complexity? I've never seen this make debugging harder, it's just an additional type.

[1] https://github.com/python/cpython/blob/d9fc15222e96942e30ea8...

dazzlefruit | 2 years ago | on: Reflecting on 18 Years at Google

Chrome is often criticized for overusing RAM. Personally I stopped using it a couple years ago, but when I stopped, it was very far from light; I remember it freezing for a few seconds for lack of RAM in a way other browsers (Firefox with multiprocessing, Edge before it got rebuilt over Chromium) didn't.

The original Chrome just felt like a barebones window to the Internet. Though I agree that Firefox et al. became much less sluggish over time. (Is that only their performance improvements or did hardware get better faster than they grew?)

Also maybe "light" and "fast" shouldn't be lumped together. Chrome can definitely be fast when it has enough resources. That and sandboxing seem to make it much _heavier_ in RAM.

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

> Simple example: a function has a parameter whose type is "variable-length tuple of int". You can pass any tuple in that is known to have 0..n elements, all of type int.

And n is fixed at the calling site, right? I wonder if something like "TypeVar, but for a list of type arguments" could solve your problem.

What's funny is that this is already kind of implemented in `typing.Concatenate`, but only for function parameters [1], not for type hint parameters.

Anyway, I would have written "a well-typed function that concatenates two arbitrary tuples whose size is statically known at the call site". Can't really remove "at the call site" or "statically known" without being ambiguous.

Edit: just found out about `TypeVarTuple`. So really we're only missing `concatenate`.

[1] https://docs.python.org/3/library/typing.html#typing.Concate...

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

> Sure, but while it's not possible to type basic functions like ones that concatenate tuples, I can say that Pythons typing system is not superior to Typescripts.

That's not really fair. The uses that make sense when considering Python's convention ("Pythonic" code, nebulous but usually well-understood) are supported.

I think what confused me about these examples is that they imply multiple values that have completely different meanings, but all get processed as equals anyway. That was before you talked about refactoring old code though.

> Okay, so how am I supposed to handle non-exceptional errors? Because using exceptions for that kind of thing absolutely isn't good practice.

If an operation on a homogeneous collection can say "nope" for some values and process others, the values would be typed "T | None". If the data isn't really a collection but a structured mapping, in general, attributes would be made optional on a case-by-case basis. If all attributes happen to be optional but the mapping itself is non-optional, that sounds more like an accident of this specific case than something we should complicate a language over. If this happens over a whole codebase, I guess I feel for you. Maybe that's when it makes sense to give up a bit of static typing and treat these values a bit more like data and a bit less like separate arguments, no matter what kind of complicated typing the language can do.

> Legacy. Typescript allows me to do refactoring of things like these step by step and very easily. Python doesn't, because it's inflexible.

> Are we taking apart my code now or what?

Honestly yes, this example seems so unusual that it doesn't make sense to debate it without knowing concretely what's happening in your code that this needs to be supported.

I guess that this specific example would get easier. But I would hardly call one type system superior over that.

Edit: my final opinion on this is "this is something that's technically possible if you follow language and conventions to the letter, but with experience you see that it's a bad idea that won't fit well with the language and you should change the design to avoid it". It happens in all languages IMO.

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

What's missing is in which context the tuple's length is variable, and in which context it is fixed. You can have a tuple size fixed everywhere (because the callee sets it) or a tuple size fixed at each call (and propagated to the callee statically).

"Arbitrary" doesn't really help because it could refer to the elements' values, to their types, or to the tuple's length. Also "arbitrary" and "variable-length" sound like synonyms to me.

Guido might use some expressions in the context of Python steering discussions but that doesn't make them less obscure for the rest of us who read C++ docs every day instead.

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

> But I have a use case for exactly this feature. Why should the language limit me? Why should I implement x functions that take different tuple lengths, with me having to choose the correct one for each use case, when I could write one function that does all?

If the PSF ran a poll for the most-wanted type checking features, I don't think this would come close to first. This sounds very niche. The people working on typing seemed very busy in the last few versions.

> Simple example - I have functions that return a Rust-like Result type, and I want to transform that into a different tuple-based format using a decorator. The transformation itself is static, but I can't write one function that handles it all, because Pythons type system is simply not developed enough. Something that would be incredibly easy in Typescript.

Returning errors as values isn't really how you're supposed to use Python though. And why is there a second tuple-based format that does the same thing?

It also smells slightly off that the rest of the code takes an object that's exactly similar to the first function's result, but with a transformation uniformly applied over the values. Shouldn't the first part's output and the second part's inputs both be clearly declared independently of each other? And then wouldn't it be an extremely niche case that both types are identical except for one transformation applied to all values? Is it worth the language complexity and a dedicated function?

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

You mean that the tuples' size is statically known at the calling site, while your message could be interpreted as the size being statically known in the callee.

I think this is clearer. The statement "arbitrary fixed-size tuples" sounded a bit like "an immutable mutable variable". It doesn't really say what's arbitrary about the tuples and in what context the size is fixed.

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

"Arbitrary fixed-size tuples" probably don't have a widely accepted meaning :) I read it as "size known when compiling the function".

The second example is cool. But I can't find a good practical use case for either example.

If you have a collection that's both heterogeneous and whose [size/key set] is statically known, when would it make sense to apply such generic transformations to them? This sounds like you have tuples or dataclasses where all elements have different meanings (since their types are fixed and different) _and_ you want to treat them like a generic collection _and_ you need the type checker to infer the result type.

The main use of tuples or dataclasses or `NamedTuples` is to pass or return values to/from functions without dealing with long lists of arguments. The elements aren't in the same category, it doesn't make sense to process them as one big collection, they mean different things.

(Also I think you made a mistake in your previous message, you wrote "an object with each key turned into a function" but it's the values that change types here.)

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

Why is pathlib bad?

Edit: I'm asking because pathlib is as good as a Python lib could be for me. Path manipulations are extremely clear and always safe. What more do you need?

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

In VSCode (which uses PyRight/PyLance) and Python 3.10:

  JSONObject = None | str | int | bool | list["JSONObject"] | dict[str, "JSONObject"]
  # this type checks
  a: JSONObject = {"a": [1, 2, "7", True, {"false": None}]}
  # this doesn't type check
  b: JSONObject = {"a": [1, 2, "7", True, {"false": object()}]}

dazzlefruit | 2 years ago | on: Writing and linting Python at scale

For the tuple example:

  from typing import TypeVar
  
  T, U, V, W = TypeVar('T'), TypeVar('U'), TypeVar('V'), TypeVar('W')
  
  def concatenate(a: tuple[T, U], b: tuple[V, W]) -> tuple[T, U, V, W]:
      return a + b
For the generic type transformation example, I'm not sure what you mean:

  from typing import Any, Callable
  
  Transformer = Callable[[dict[str, Any]], dict[Callable, Any]]
This seems to match your question but it's really weird.

dazzlefruit | 2 years ago | on: Reflecting on 18 Years at Google

The Chrome versions of the first few years were so nice to use. It was the _lightest_ major browser for a time. It's insane how it has drifted since then.
page 1