Live data from Hacker News

Tests aren’t enough: Case study after adding type hints to urllib3

sethmlarson.dev

121–130 of 205 posts

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#121
post #64
post #32

Earlier quoted context omitted.

How in the world does type checking lower the ceiling on excellence?

Tbh I'm conflating multiple things, I've heard a lot of good things about Haskell. But in C# for example, if the system was not designed with dependency injection and everything being an interface it's very hard to build a test harness since you can't mock anything. Which means everything has to be tested manually. (I haven't done any C# in a long time, maybe it's not the case anymore) So you have to create an interf…

> But in C# for example, if the system was not designed with dependency injection and everything being an interface it's very hard to build a test harness since you can't mock anything. Which means everything has to be tested manually.

I wouldn't have put it like that, but I think I know what you mean. Mocks for unit testing do require that you have defined an interface to implement, which means that every class that you want to be mocked out needs to have an interface extracted. It is extra work. Overall I think the tradeoff is worth it, myself, especially if your IDE can automated extracting an interface. But it is dumb work that a smarter language/type system could avoid.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#122
post #28

I love static typing/type hints if for only 1 thing - code maintenance. Even code I wrote six months ago. Not having to dig through 6 functions deep to try to figure out whether "person" is a string, or an object, and if it's an object what attributes it has on it etc. is huge. And not to mention that some clever people decide - hey, if you pass a string I'll look up the person object - so you can pass an object or a…

I never understood this argument. In what kind of shop are you working that passing a string named person to a method expecting an object is tolerated. Or even passing different types that don't share a common interface. This would never fly in a code review in any of the companies I've worked for.

You'd think. But I've seen many many many examples of this pattern in production JS code.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#123
post #28

I love static typing/type hints if for only 1 thing - code maintenance. Even code I wrote six months ago. Not having to dig through 6 functions deep to try to figure out whether "person" is a string, or an object, and if it's an object what attributes it has on it etc. is huge. And not to mention that some clever people decide - hey, if you pass a string I'll look up the person object - so you can pass an object or a…

I never understood this argument. In what kind of shop are you working that passing a string named person to a method expecting an object is tolerated. Or even passing different types that don't share a common interface. This would never fly in a code review in any of the companies I've worked for.

In C++ you're only ever one missing "explicit" from introducing such problems.

Suppose I call fire(bob). Programmers from other languages might reason that since fire is a function which takes a Person, bob must be a Person. Not in C++. In C++ the compiler is allowed to go, oh, bob is a string and I can see that there's a constructor for Person which takes a string as its only argument, therefore, I can just make a Person from this string bob and use that Person then throw it away.

To "fix" the inevitable cascade of misery caused by this "feature" C++ then introduces more syntax, an "explicit" keyword which means "Only use this when I actually ask you to" rather than as a sane person might, requiring an implicit keyword to flag any places you actually want this behaviour to just silently happen.

This way, hapless, lazy or short-sighted programmers cause the maximum amount of harm, very on-brand for C++. See also const.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#124
post #97

Earlier quoted context omitted.

Hm, growing somewhat experienced, I find myself adapating an old quote more and more: Sufficiently advanced static typing is indistinguishable from dynamic typing. Now, I know, it's not true. It's entirely possible to build weird things in python that are provably impossible to typecheck statically. But modern language servers and their type inference capabilities in rust, terraform, or even straight up python are ve…

> Sufficiently advanced static typing is indistinguishable from dynamic typing. Static typing type checks are compile time, dynamic typing doesn't. I don't see how these two could be indistinguishable, in one you can't run the program with type errors, in the other you can.

I believe the author was talking about the act of writing the software. Modern type inference means that you can mostly code without needing to write down the types in many cases. This line of code is the same in JavaScript or C#:

    var instance = new SomeClass();
In function definitions, where you are definitely going to need to provide parameter types it's extremely common to document those types in a docblock in a dynamically typed language. At least I always did. So making the types part of the definition is not a significant difference while writing the code.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#125
post #97

Earlier quoted context omitted.

Hm, growing somewhat experienced, I find myself adapating an old quote more and more: Sufficiently advanced static typing is indistinguishable from dynamic typing. Now, I know, it's not true. It's entirely possible to build weird things in python that are provably impossible to typecheck statically. But modern language servers and their type inference capabilities in rust, terraform, or even straight up python are ve…

Please write a type for the following function: def compose(start, *args): def helper(x): for func in reversed(args): x = func(x) return start(x) return helper There is no mainstream typed language which can write a fully general type for the vararg compose function. TypeScript is probably the one that comes closest, but last I checked it still was unable to write a sufficiently powerful array type. You can write a t…

I think you can in rust as long as args is a slice. Rust doesn't have varargs except for c interop. A slice or Vec of function pointers is the idiomatic way to do the same thing.

Something like:

  fn compose(start: Box X>, args: Vec T>>) -> Fn(T) -> X {
    move |x: X| {
      let mut x = x;
      for func in args.iter(). reversed() {
        x = func(x);
      }
      start(x)
    }
  }

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#126
post #97

Earlier quoted context omitted.

Hm, growing somewhat experienced, I find myself adapating an old quote more and more: Sufficiently advanced static typing is indistinguishable from dynamic typing. Now, I know, it's not true. It's entirely possible to build weird things in python that are provably impossible to typecheck statically. But modern language servers and their type inference capabilities in rust, terraform, or even straight up python are ve…

Please write a type for the following function: def compose(start, *args): def helper(x): for func in reversed(args): x = func(x) return start(x) return helper There is no mainstream typed language which can write a fully general type for the vararg compose function. TypeScript is probably the one that comes closest, but last I checked it still was unable to write a sufficiently powerful array type. You can write a t…

Vararg functions also have limited use. Especially considering that most of the time, your args will all have the same type, and therefore could just be passed in an array or similar. The one mainstream exception I know of is print functions, and we have* ways to statically check those.

Your toy example, even generalised, has no practical use. If I can write this:

  compose(f, f1, f2, f3)
Then I can write that instead (Haskell):

  f . f3 . f2 . f1
Or this (F#):

  f1 |- f2 |- f3 |- f
And now we’ve reduced the problem to a simple function composition, which is very easy to define (Ocaml):

  let (|-) f g = fun x -> g (f x)
  (|-): (’a -> ’b) -> (’b -> ’c) -> (’a -> ’c)
This generalises to any fold where the programmer would provide the list statically (as they always would for a vararg function): instead of trying to type the whole thing, just define & type the underlying binary operation.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#127

I love static typing/type hints if for only 1 thing - code maintenance. Even code I wrote six months ago. Not having to dig through 6 functions deep to try to figure out whether "person" is a string, or an object, and if it's an object what attributes it has on it etc. is huge. And not to mention that some clever people decide - hey, if you pass a string I'll look up the person object - so you can pass an object or a…

The main argument for dynamic typing is speed in prototyping but I find that's opposite for me. I'm much more comfortable rapid prototyping and ripping stuff apart when I have a strongly static typed environment telling me what I just broke. Doing radical refactoring often involves just making those changes and then fixing all the IDE or compiler errors until it runs again.

> when I have a strongly static typed environment telling me what I just broke.

Yes, I'm a total scatterbrain. Types let me remind myself later that I did in fact forget what I'm doing and what I did. It lets past-me protect future-me.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#128
post #57

Earlier quoted context omitted.

The main argument for dynamic typing is speed in prototyping but I find that's opposite for me. I'm much more comfortable rapid prototyping and ripping stuff apart when I have a strongly static typed environment telling me what I just broke. Doing radical refactoring often involves just making those changes and then fixing all the IDE or compiler errors until it runs again.

I would argue that the really big benefit of dynamic typing is that it enables a really nice interactive interpreter shell experience. I think it's also important from a prototyping standpoint that Python's static typing model does a lot of inference -- you don't have to add an explicit type annotation on every single variable.

> I would argue that the really big benefit of dynamic typing is that it enables a really nice interactive interpreter shell experience.

I use the Python REPL quite often, and have non trivial experience with Lua’s. But the best experience I’ve ever got was with OCaml: I type the expression, or function definition, without giving type annotations, and I get the type of the result in the response.

You wouldn’t believe the number of bugs I caught just by looking at the type. Before I even start testing the function. And that’s when I don’t have an outright type error, which a dynamic language wouldn’t have caught — not before I start testing anyway.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#129

I love static typing/type hints if for only 1 thing - code maintenance. Even code I wrote six months ago. Not having to dig through 6 functions deep to try to figure out whether "person" is a string, or an object, and if it's an object what attributes it has on it etc. is huge. And not to mention that some clever people decide - hey, if you pass a string I'll look up the person object - so you can pass an object or a…

The main argument for dynamic typing is speed in prototyping but I find that's opposite for me. I'm much more comfortable rapid prototyping and ripping stuff apart when I have a strongly static typed environment telling me what I just broke. Doing radical refactoring often involves just making those changes and then fixing all the IDE or compiler errors until it runs again.

the main argument for dynamic typing in particular in the context of object oriented programming is decoupling. It is always the receiving object's responsibility to handle whatever they get.

if you write dynamic OO languages with a static mentality in mind, i.e. you try to enforce some sort of global type expectation before the program runs, then obviously static languages are better, because you're trying to write static code.

Benefiting from dynamic languages means ditching that mindset altogether.

Re: Tests aren’t enough: Case study after adding type hints to urllib3

#130

Earlier quoted context omitted.

> Honestly will never go back to languages without type checking, it prevents so many bugs and is a huge help in understanding code you haven’t worked with previously. I see static types as one of the most powerful communication tools around, as far as code goes. I can't relate at all to people complaining that they waste time. They must work very differently from how I do, is all I can figure. It's that, or they don…

Consider these 4 possible combinations for programming languages: (1) Low-level, static types (2) Low-level, dynamic types (3) High-level, static types (4) High-level, dynamic types For whatever reason, historically #1 and #4 have been most popular. C, C++, Pascal, Ada, and Java are #1. Python, JavaScript, Perl, and BASIC are #4. There haven't been a lot of #2 or #3 languages. Some #3 languages (TypeScript and Python…

Exactly. IMO we still don't have a good #3 language - in particular, all of the popular languages that can be compiled into native code are in category #1.
Post reply on HN