Live data from Hacker News

Things I Was Wrong About: Types

v5.chriskrycho.com

91–100 of 468 posts

Re: Things I Was Wrong About: Types

#91

Earlier quoted context omitted.

What are you working on though? What algorithmic structure?

Compare this: private static function request(?string $method, ?string $url, array $options): array { ... } To this: function request($method, $url, $options) { ... } I can grasp the latter much better. It immediately forms a structure in my head that I will remember while I read other parts of the code. To do the same with the former, I think my brain uses up twice the energy or more. And even then, I will not have…

Got that, but was triggered by ‘algorithmic structure’; that sounds a tad ‘over the top’ when you then come with some basic web request.

However, I would not call your example particularly well typed. Array is still basically untyped.

When I talk about types I mean things like;

     Either AddGroup(string GroupName, User[]? users)
In this case I know what I am getting and I know the users are already, when reaching the webserver, deserialized, validated (or the type would reject them and produce an error and I know I am getting an actual Group back.

in your case that would be;

      function AddGroup(GroupName, Users)
I have no idea how that is better or clearer or less work to write? No idea what it returns; Users probably is an array of users, but is it? Or is it a typo? And I need to validate whatever comes in.

You can add docs but I do that still with the typed version (although thats automated mostly unless I need to explain something).

Worse as well is that when I have this:

      function AddGroup(GroupName, Users)
      function AddGroups(GroupNames, Users)
Now i'm completely lost. I'm not even sure these users as input are the same things? Both arrays? Both the same User 'things'? Output?

But yes, in php I write typeless mostly too (although I luckily do not have to touch it much anymore) as the types mostly suck. But in more advanced type systems, the types will tell you a lot/all about the input/output, so you can do without a lot of docs and trying things out can be automated; as we know the precise input so some generator can generate example input that will immediately work; like swagger on speed.

Etc. But agreed, your example does not benefit too much from typing, however I would define Option[] as something precise than just a void* and hope for the best. Also Method and URL so I know invalid input for those very well defined things cannot be violated.

So your example:

    private static function request(Method $method, URL $url, Option[]? $options): WebResult
would clear up a lot for me. Now, for instance, I can see, and not guess, that you are answering a web request instead of some request with confusingly similar names to webrequests.

But how about some more 'concrete' examples; 'request' is rather a low level / abstract thing (I hope...). But your business logic would contain more concrete functions; any examples from those that are similarly badly geared to types?

Re: Things I Was Wrong About: Types

#92

Earlier quoted context omitted.

In many langauges it is possible to have complex types that are pass-by-value. Rust also completely solves the mutation issues with pass-by-reference by putting the mutability of references in the function signatures and only allowing one mutable reference at a time.

But still, I think it does not fully solve the architectural issue or encourage good architecture (though it can certainly help reduce bugs)... In this case you may end up with lots of duplicate instances in different blackboxes which may not be a good thing either. The point of good state management is to ensure that each instance has a single home. As soon as you start passing instances between functions/modules/co…

It really depends on the language. Some still help you use this case in an amazing way. For example rust will allow you to create an enum which can be a FooId(&str). (Or add extra 4 lines to get an owned String that's immutable)

Now you've got an immutable id string, you can access as easily as the bare one, but now you can't mix it with other types of IDs, so you won't pass it to something expecting BarId by accident. As a result - no black boxes and a clearer design.

A variant of this is the cause of many Linux kernel issues. They basically had to cram it into macros to prevent passing real/kernel pointers to userspace by accident, because pointer is a pointer is a pointer.

Re: Things I Was Wrong About: Types

#93
This is the opposite of my progression. I worked professionally with Haskell for 6 years and then Scala for 2 years at the early stages of my career.

Static typing is a waste of time that does not facilitate better design, better clarity or safer code. The classes of errors detectable and preventable with static typing are almost never very important, and in dynamic languages you can achieve the same results with lightweight unit tests that are no more effort, and often much less effort, than creating or maintaining type system designs.

Type annotations are code and more code is just more liability and more complexity. It really is just that simple. You should strive to write code with the fewest cognitive concepts necessary. Cognitive concepts are the hugest form of tech debt you can have in code, much much worse than repeated code, lack of modularity, lack of test coverage and so forth. Type system design invites running amok not just with all kinds of new cognitive concepts to model every domain problem, but also the code authors are encouraged to use “creativity” when developing these concepts, which leads to poorly factored messes that express the limited view of a set of specific people - often with huge premature abstraction that makes the code excessively brittle (even when it’s claimed to be extensible) and poorly suited to accommodate use case changes or requirements changes.

For the past 5 years I’ve moved on to exclusively using the Python ecosystem for system designs. It’s nice since I work in machine learning and no ecosystem even comes close to Python in terms of ML tooling, but even for backend systems and web service designs around the ML product my team creates, using Python has been pure joy in terms of easy development cycles, easy ability to write less code, easy ability to achieve high safety and reliability, easy ability to trade off safety as a resource to accommodate sudden business requirement changes.

I can sincerely say across the board programming large backend systems in Python has been more pleasant, easier to design, read, understand and maintain, easier to extend, and led to safer, more reliable delivered solutions than any of the projects I worked on using Haskell or Scala (even when working with those languages in mature organizations that had very seasoned, veteran functional programming experts establishing in-house development practices).

Now that I’ve transitioned through senior & staff engineer and became an engineering manager, my perspective over the years has come to endorse dynamically typed languages as vastly superior tools.

My experiences with Haskell and Scala just lead me to believe Python is strictly better for large system design.

Re: Things I Was Wrong About: Types

#94

Earlier quoted context omitted.

Counter point: with the former, I know exactly how to use it: for item in request(“get”, “google.com”, []) { .... } Whereas if there’s no example for the latter, I’d have to read the code of that function (or sometime the code of the functions it called) to know how to use correctly.

Wishful thinking. You know it returns an array, but you don't know what the array contains. So you don't know how to use it. You still need to look into the code to see what it returns. Then you will see that it returns an array and what the array contains. And now you had to process the information that it returns an array twice. Once in the function definition and once in the function body.

> You know it returns an array, but you don't know what the array contains. So you don't know how to use it.

That's a limitation of your specific example, not type systems in general.

Re: Things I Was Wrong About: Types

#95
> Type inference: because having to write out every type, however obvious, is an incredible waste of time. Person me = new Person(); is ridiculous. let me = new Person(); may seem like a small improvement, but spread over the body of an entire program and generalized to all sorts of contexts means that type annotations become a tool you employ because they’re useful — for communicating to others, or for constraining the program in particular ways — rather than merely because the compiler yells at you about something it should know perfectly well.

Type inference was a major revelation to me as well in Rust. I was reluctant to learn the language because of my experience in Java with its high ceremony everywhere, mostly due to lack of type inference.

The first thing I noticed with Rust was type inference. It gives the entire language a distinctly high-level, almost scripting language feel - modulo ownership.

Re: Things I Was Wrong About: Types

#96
post #71

Earlier quoted context omitted.

>2. When you want to implement a method which applies to multiple libraries, you end up writing `fooString`, `fooInteger`, `fooDatetime` etc. DRYing is too hard. So you end up writing 10x more methods As discussed in the article, you want a sum type here. >3. No you don't avoid `null` checks at all. Entirely language dependent. >4. Generalizations are harder to implement. For some definition of harder. Harder to impl…

How many languages have true sum or union types though? For example, in Haskell I can’t declare a function as (foo: (String | Int) -> Int) and then call (foo “bar”) or (foo 123), I need to create some kind of wrapper type (like Either) to contain the possibility of a String or Int. If this were possible, there would not be such a proliferation of useless types throughout code, and code could be updated incrementally…

Haskell's Either is a 'true' sum type, it corresponds exactly to the way sums have been defined in the literature for decades, and also is the Curry-Howard representation of logical or. It necessarily must be inside a Either-like wrapper in order to be type safe, String and Int are ultimately different and so at some point we must discriminate between them. foo could call further functions with its argument inside itself accepting Either String Int, but eventually a destructor will be hit somewhere in the call stack.

If String and Int do implement the same behaviour under some circumstance, then a typeclass should be used to define that behaviour. Then foo can have type (Bar baz) => baz -> Int, where String and Int have Bar instances.

Re: Things I Was Wrong About: Types

#97
post #50

I don't really see most of my code being improved that much by types and I also see a lot of extra complexity that people in the sphere I am (indie games) have to deal with when using typed languages. It just doesn't seem worth it. When you have to spend a lot of time fighting your language, and handling numerous extra concepts that don't exist in an untyped language because they don't need to, it feels like the peop…

Aren't you falling into the same trap that the post explains? That there are nuances around when types are useful and not useful? An indie game developer might spend a lot of time designing methods of gameplay and artwork for the game, but that doesn't mean that types should go out the window for all levels of programming. Wouldn't it be better to approach this by which problem we are trying to solve? A script that i…

This seems likely. It took me embarrassingly long to realize the fact that you can't understand the benefit of a feature you don't understand. Seems like an obvious tautology, but it's one I fell for over and over, and one I think grandparent is guilty of here.

Re: Things I Was Wrong About: Types

#98

To play against the current wave, and give a contrarian pov: I'm grown up with statically typed languages from C/C++/C#/Java and later and didn't know about dynamic languages till recently. 1. Which types are we talking about? - In earlier static-typed language, the processor-based types `uint64` looked very strict, optimized the code for hardware architecture. - Having mathematically and ontological correct types is…

Dynamic languages don't do away with such types, especially uint/float, etc. They just hide it away from you. The only thing it gives you is false confidence, where one day you end up doing operations on two floats because your untyped function is supposed to get numbers in, and you're left with 36.99999999999994$ on your bank account because it wasn't explicit. So, you write unit tests to make sure that you only pas…

I'm not following your answer to 2, do you have an example?

Re: Things I Was Wrong About: Types

#99
post #3

I followed a similar trajectory. Types were the bane of my early career. Hideous, extraneous. But really, they're the light at the end of the tunnel once you've worked your way though the dynamic / weak typing minefield. It took me a lot of Python, Javascript, and Ruby for me to get there, but now I'm way more comfortable on the other side. The correct type system is actually way more expressive than not having stron…

Same here! I've started with C++ and Java, learned to hate excessive typing, went through a long period of dynamic typing, and now I'm at the point you and the the author are. I still code a lot of Common Lisp on the side, but my Lisp code now looks entirely different than it looked just 3 years ago. The language standard does support optional typing declarations, and there's an implementation (SBCL) that makes use o…

"statically, strongly typed Lisp that still doesn't sacrifice its flexibility and expressive power"

SML

Re: Things I Was Wrong About: Types

#100

Earlier quoted context omitted.

Agreed and we have to continue improving in every way; dynamic/static/hybrid, just saying that I have not seen this dynamic enlightenment in larger projects. I have only seen the pain of runtime errors that other (static language) teams never had. Sure, if you would-have-written a test for it, you wouldn't have had it either, but types rather force you to think about it while writing. So sure, you are 'done faster',…

The view I've heard expressed is that deep thought on a piece of code reduces bugs. Whether that takes the shape of religious TDD, rigorous proofs or detailed type design doesn't make such a lot of difference. I used to be fully bought into types, but I've since realised that they have a number of downsides that in many cases more than offset their benefits: 1. Ergonomic typesystems require a lot of work to happen at…

I am positive about a lot of these points for the future. Especially the performance points; that's going forward fast. But yes, that's often pretty slow; not that bothered by it for my work though. Also, linters work well for statically typed languages too; I usually don't have to compile for 100s of lines of code. If the editor does not complain, it'll probably all work fine. Like I said; do what works for you , but I think at least a good mix will get more benefits.

6. People mention this more often, but I just don't see how that works; you cannot program without knowing what data you are getting. Sure the world is not typed, but at the moment you are going to use the data, it is typed; be it in your logic, head or actual types. Any webserver can go lower level and give you a ByteStream, but when you finish parsing that, you still have types. You might not know them upfront, so you use ByteStream for a bit, but once you know, you bake types and the world is nicer. Imho :) Not sure why that's a difference?

7. This is an issue where? I know it's Erlang domain, but microservices/docker/k8s/ci/cd/lambda/functions/.../all modern crap do this (redeploy, killing the previous instance(s)) with any code, always, including dynamically typed code. So sounds like a niche?

8. Agree with this; we should experiment and research these things and continue building them. I work with Lisp/Clojure as well and like it, I just miss types often. I never suggested it's all crap; I'm just looking where benefit comes from.

Post reply on HN