12 ms·
For the most part I feel that for 99% of problems types just “get in my way”. It feels like I already know what is going to go in where, and having to type it
by Zyst 9y ago
For the most part I feel that for 99% of problems types just “get in my way”.
It feels like I already know what is going to go in where, and having to type it is just a waste of time.
That said, when I have to work with APIs that can not guarantee their data integrity throwing in a quick @flow annotation of top of the file, and taking the time to write out what is optional has proven very valuable to make sure my functions, and subsequent code is not going to throw.
Another reason I like flow more is because I don’t need to convonce my team to use it, I can just use it myself, and then delete it after I am done writing my code.
- Zyst 9y agoAs a side note: The TDD-ish alternative to this, which I also use sometimes is just adding a test where you pass in undefined on your optionals, and then iterating until the test stops throwing.
- pdpi 9y agoUnfortunately, “has no obvious bugs” is not quite the same as “obviously has no bugs”. TDD gives you the former, static typing the latter (within what can be represented by the type system, of course)
- kerkeslager 9y agoYes, but "within what can be represented by the type system" is a fairly large caveat. Type systems often can't represent intent. int* increment(int* i, int step) { return i + step; } Is there a bug here? Well, that depends on the intent. Did we really intend to add an int to an int*? Even Haskell's type system can't tell what you intend: increment i = i + 11; Did we really intend to add 11 rather than 1, or is that a typo? A unit test would clarify the intent of both these functions, and catch both bugs fairly reliably (if they aren't the intended behavior). Ultimately I think TDD and types guarantee different things, and both are useful/needed.
- naasking 9y agoYou have to be willing to use types to express intent. Types are logical propositions, so you have to encode your proposition as a distinct type. You can actually prove many programs correct by exploiting even Java's poor type system: Proving Programs Correct Using Plain Old Java Types, http://lambda-the-ultimate.org/node/5387 http://lambda-the-ultimate.org/node/5387
- kerkeslager 9y agoOkay, can you say how you would reasonably catch the typo in my second example with types? Obviously you have to be willing to use types to express intent, but even if you're willing, types are limited in what kinds of intent they can express.
- wtetzner 9y agoint* increment(int* i, int step) { return i + step; } I think your example is flawed. The real problem here is that you're allowed to add an int to an int* with +. int* is not be of type int, and should therefore require some sort of cast to make it possible to add an int to it. Either that, or require a different operator/function to add to a pointer, e.g.: int* increment(int* i, int step) { return prt_add(i, step); } Not all type systems are equal, and how the type system interacts with the language is important.
- kerkeslager 9y agoThat's my first example, not my second. I specifically asked about the second example because it's fairly obvious that C's type system is garbage. To be clear, my question is, how would you reasonably catch the typo with types in the following Haskell function? increment x = x + 11; ...noting that this typo is reliably caught by a unit test.
- tome 9y agoHow do you catch this broken unit test? def test_increment(x): assertEqual(increment(x), x - 1) The answer to your question is, you can't. In this case the implementation is the specification. You're going to have to set a more meaningful challenge.
- meuk 9y agoAs pointed out in the video, types can be helpful for bigger projects, where others have to read your code, and sometimes for refactoring.
- kerkeslager 9y agoI think what you're talking about here is static types getting in your way, not just types getting in your way. Strong/weak types and dynamic/static types are really two different spectrums. People conflate strong types with static types and weak types with dynamic types, but they aren't really the same thing. Static/dynamic just has to do with whether the types are checked at compile time or run time. Examples: Static: C, Haskell. Dynamic: Javascript, Python. Weak/strong has to do with what kinds of checks the type system does. A strong type system is capable of checking a lot of different things for you. Static types are often stronger, but not always: for example, C is statically typed but its type system checks hardly anything: int* + int is perfectly valid. A list of programming languages from weakest-typed to strongest-typed might look something like: JavaScript, C, C++, Common Lisp, Perl, Scheme, Ruby, Python, Java, C#, OCaml, Haskell. Static types are nice for projects which will grow large and where bugs are a big problem, but I think for the average HN person, static types aren't really necessary. Strong types, on the other hand, are extremely useful. Even in a dynamically typed language, they aid in debugging a lot, because type errors occur much closer to where they're caused. In Python, for example, `"foo" + 42` immediately fails. But in JavaScript, you don't get an error until much later, perhaps when your webpage is mysteriously displaying "foo42". Of course, there's a small cost to strong types: in the case where I actually do want to append a number to a string, I have to do `"foo" + str(42)`. But I think people tend to overstate this cost because it's visible. But if you look at the big picture, typing five extra characters takes a lot less time than debugging almost anything.
- naasking 9y agoStrong/weak is generally not a useful distinction because it has no formal meaning. What you're probably after is "expressiveness" and "soundness". So C's type system is unsound and it's types are inexpressive. Haskell's type system is sound and moderately expressive. Agda's type system is sound and expressive.
- kerkeslager 9y ago> Strong/weak is generally not a useful distinction because it has no formal meaning. "This doesn't have a formal meaning, therefore it's not useful" is quite a logical leap you've got there. I've got over a decade of professional programming experience in which "strong types" is a useful enough concept to help me do my job. "Soundness" certainly gives stronger guarantees, but it's more than I've needed. I'm not aware of "expressiveness" having a formal meaning, but my informal definition has functioned for me so far.
- walshemj 9y agoDon't take this the wrong way, but I suspect you are young developer who hasn't realised that putting in the extra work up front pays large dividends down the line.
- nogridbag 9y agoOr perhaps there is no correct way to write software and is very dependent on the problem domain and the type of application. I wonder how many projects went over budget, are incredibly over-engineered and ultimately failed because some senior developers read a book on DDD and went crazy with types.
- nawitus 9y agoThe types will make reading the code multiple times easier when someone else reads the code. Or when you go back to the code six months later.
- filterfish 9y agoIt's hard to overstate this point.
- skohan 9y agoI used to feel this way, but the benefits of a good type system far outweigh the drawbacks IMO. A lot of runtime errors caused by dynamic types get pushed back to compile time, which is a much safer and easier time to deal with them. Nowadays writing code without type checking feels like building a house on quicksand. There's also the self-documenting aspect of strongly typed languages: for instance, if I look up the documentation on a javascript API, I have to hope function parameters have been specified well, otherwise I just have to guess what should be passed in or dig through the source. With a strongly typed language I probably get that information as part of the autocomplete hint. And good type systems can be powerful tools. In Swift for example, the protocol system is powerful enough that I'm sure it results in writing less code overall, not more.
- flavio81 9y agoPlease don't assume that the problems that are specific to Javascript also happens with other dynamically typed languages.
- spraak 9y agoBut why don't they? Aren't the problems nearly the same? And if not, how?
- carlmr 9y agoThey do, JavaScript is just the worst offender. Python has the dynamic typing problems, but is better because it has a somewhat strong type system. It will tell you when it's wrong, but often too late (oh this function only gets called wrong once every 100h on my server, thank you for telling me now that it went wrong). And sometimes different types can duck into the same function (e.g. string and list are both iterable). So yeah, Python suffers from the same issues when you want to scale your code, but it's A LOT less bad than JavaScript.
- flavio81 9y ago>But why don't they? Aren't the problems nearly the same? And if not, how? Concrete example: Common Lisp is very strongly typed. So it almost never automatically converts from type to another, except when it makes total sense (i.e. the square root of -1 will return a complex number). So, if there is a type mismatch, it will be caught (at runtime) raising an exception. Now, you would think "yeah but my statically typed language will check this BEFORE the code runs". Yes, but in Lisp, the exception doesn't terminate the execution, it enters a mode in which the system asks you what to do next. Thus, what you do is, you go back to the source code, to the function with the error, you correct that specific function, compile that specific function (which happens almost instantly), and then resume execution of your program. This means that the previously "invalid operation" will be run again but with the new definition of your function, thus the code will continue running without said bug. So, all in all, it's very nice to use...
- mbrodersen 9y agoFor the most part I feel that 99% of problems are easier to fix with types. Especially large scale refactorings.