25 ms·
The type system is a programmer's best friend
- henrydark 4y ago> ... to prevent silly mistakes like multiplying $100 with £20 Honest question, what should multiplying $100 with $20 give?
- yakshaving_jgt 4y agoAn error at compile time.
- usea 4y agoA type error.
- musingsole 4y agoProgrammers will have immediate answers for you -- stated confidently as if to imply there is a spec somewhere when in fact no spec exists and the programmer you're talking to is peddling their own bullshit as gold.
- creata 4y agoA dollar times a dollar is a dollar squared. You don't need a spec for that! For example, if you have a random variable that's in dollars, its variance would have units of dollars squared. People consider the variance of dollar estimates all the time.
- henrydark 4y agoAnd dollars times pounds is similarly a unit of covariance
- deleted 4y ago[deleted]
- Smaug123 4y ago2000 square dollars? If I'm choosing between ways to spend capital so as to improve the efficiency of a process, and that process currently produces five widgets per dollar, then the quantity I'm comparing to choose between my courses of action can be measured in widgets per square dollar. Hiring a better engineer for more money may create an efficiency improvement of 1 widget per dollar, with an outlay of $1k extra for the better engineer, giving a total gain of 0.001 widgets per square dollar; hiring a worse engineer for much cheaper may represent an improvement of 0.1 widgets per dollar, at an outlay of $1, giving 0.1 widgets per square dollar. Perhaps not the most intuitive unit, but it's not impossible. (Though since you can even measure it in dollar-sterling if you like, I suppose that doesn't make it a counterexample to "stop multiplying dollars by sterling".)
- an1sotropy 4y agoIs this getting downvoted? bummer. You have provided a real example, which I was looking for, of why one might need to express a square dollar; thanks. I wonder if the people who want to argue "types save you from bugs" see your example as very unwelcome, since they'd want to use "squared dollars" as an example of something nonsensical that should be flagged as a type error. I hope those people can reflect rationally on the limits of type systems in the real world.
- usea 4y agoI also enjoyed the square dollars example. A type system is merely a tool to encode information to help better model things. If you want to prevent multiplying dollars together, types can help you do that. If you want to enable multiplying dollars together, types can help you do that, too.
- Spivak 4y agoBut it shouldn’t be an error in any unit system. # oops my scaler has a unit x unit * x unit = x unit^2 The value isn’t catching this line of code since it’s potentially valid. It’s catching the line of code where you pass the result to a function that expects unit.
- masklinn 4y agoThe same as multiplying 100 radishes by 20 radishes.
- Emigre_ 4y agoComputer says no
- Ygg2 4y agoIf both values are decimals? 2000.
- zasdffaa 4y agoPounds sterling
- remram 4y agoThey probably meant adding
- jstimpfle 4y agoThere isn't a reason why you shouldn't write something like $100 * (£20 / £47) as "int dollars = 100 * 20 / 47;". Note that this expression assicates to the left instead of the right, which can be the right thing to do if doing integer arithmetic. But it would not work with a strongly typed setup as in your example. In my experience trying to prevent accidental mistakes is a waste of time and often makes our lives miserable. Catching the rare bug by doing complicated work in the type system when it would have been easy to find in normal code anyway is not worth it.
- xigoi 4y agoBut why would you use integer arithmetic when dealing with fractions?
- jstimpfle 4y agoIt was just the first example from the top of my head. The expression above calculates the right thing, cast to int. In general, prescribing which units we can multiply and which not, is extremely silly if you consider how we learn it in school. You can multiply anything and everything, simply take care of the units. There isn't an obvious reason why we couldn't have 2000 dollar-pounds as a transient value in a longer computation. The real problem is that most type systems aren't fit to track the units automatically. Solution: Don't beat yourself up, track the units in your mind / in comments / in variable names instead of the type system. And just get it right. It's not that hard - if you mix something up that's usually the type of bug that is immediately noticed and fixed.
- barbariangrunge 4y agoThe main benefit of static types is great refactoring tools
- Jach 4y agoIt's amusing that the first language with great refactoring tools had dynamic types (Smalltalk). But I think it's an underappreciated note that static type languages often create a bias for earlier coupling, less system-independent modularity, and a lot of unnecessary data copying from structure to structure, creating in turn a stronger need for refactoring tools since what should be small changes end up becoming larger ones affecting more places. Even though they can't catch everything (thanks to reflection) I'm pretty happy that such tools exist when doing Java development. I've sometimes missed them in less tooling-mature dynamic langs, but also have found them less necessary. (Though I'm sure part of that is due to other feature-factors, like closures, that historically have been a long time coming (if ever) to the most popular static langs.)
- bjourne 4y agoI don't understand. Isn't classes what the author asks for?
- masklinn 4y agoLots of languages reify types as classes, but that's not necessary. You don't need classes to have types. Using classes also leaves entire segments of type-expression on the table or makes them unwieldy e.g. https://en.wikipedia.org/wiki/Tagged_union https://en.wikipedia.org/wiki/Tagged_union So it's orthogonal, classes are just one possible implementation of types.
- eyelidlessness 4y agoDepending on the language yes, or probably, or definitely not. A class is one of several ways to represent a type. Some languages have structural type representations and a class/instance is equivalent to a “plain old ___ object”; some languages have types but no notion of classes at all.
- bjourne 4y agoSure but the requirements given by the author are exactly represented by, for example, Java's class system. So why is the author dreaming about something that has existed for 20+ years?
- tsimionescu 4y agoClass as a concept is somewhat orthogonal to type, at least to people into programming language research, who consider "type" to implicitly mean compile-time type. Classes are taken to refer to support for virtual method dispatch, while types refer to compile-time expression type-checking. In languages like Java, C++ or C#, the type of an expression corresponds to the class of the value it will have at runtime. However, in Python for example, compile-time expressions always have the type "any", but can have various classes at runtime. This difference between class and type can even be seen in some of those languages. For example, the type *X has no corresponding class in C++ for any X. In Java, the type int doesn't have a class, and the types ArrayList<Integer> and ArrayList<Object> have the same class.
- mikewarot 4y agoI once attended a meeting where a Professor from a University somewhere in Chicago gave a brilliant demonstration of using a similar type system for dealing with values in Electrical Engineering. It made quite sure you couldn't do things like add volts and amps. [Edit] it also handled things like parallel resistances, etc. It was in C++ if I recall correctly. This is a great idea, that I've haven't had cause to use yet.
- masklinn 4y agoFWIW that's a pretty basic application called "units of measure". Some languages like F# have that natively.
- mrkeen 4y agoI wish static typing were as uncontentious as units of measure.
- masklinn 4y agoI wouldn't say that UOM are uncontentious, things can get dicey around reference units and precision for instance, or the combinatorial explosion of composite units.
- mrkeen 4y agoRight, but if you told your professor you sometimes represent distance in Volts (to make your calculations simpler) you'd get some funny looks. You could even double-down on it: "Have there been any studies that prove that using units of measure helps you get the right answer?"
- tsimionescu 4y agoFunnily enough, there are some applications for converting mechanics problems to analogous electricity problems to leverage circuit simulation software such as PSPICE to help solve things like transcendental equations.
- mhaberl 4y ago>A string value is not a great type to convey a user's email address or their country of origin. So we have a type for "country of origin". And then some country that you have in the records splits up into 2 countries, what do you do then? Do you keep a list of all countries that ever existed and keep it up to date? This approach works good in some cases, but not always
- Ygg2 4y agoThat's an unrelated problem, that's outside the scope of model presented. Remember the physicists adage: All models are wrong, some are useful. You can have your name change as well, or your calendar can change, or etc. Does that mean we stringly type everything? No. You model changes either via a separate field(s) or some kind of change table.
- musingsole 4y agoIt's a very related problem. I agree: All models are wrong; some are useful. A string is a wrong model for an email address. But it's a pretty useful one. A custom type sitting lonely in an isolated codebase IS ALSO A WRONG MODEL. Arguably, it might be more a useful one than a string. But that's debatable. And on that debate, I'll argue a string is a better model because it is a better UNDERSTOOD model by more PEOPLE than whatever MyEmailClassForThisProject you just came up with.
- Ygg2 4y ago> It's a very related problem. It's not. You have the same problem regardless of type you place there. > A string is a wrong model for an email address. But it's a pretty useful one. Depends on use case. Perhaps it's an overkill in this toy example. I've real life use cases with untrusted user input where having raw string as untrusted and some kind of verified type as trusted would eliminate whole swath of errors.
- mhaberl 4y ago> You can have your name change as well, or your calendar can change What do you mean by that? Having a type for "country of origin" would mean that the type gives you limits on what values it can hold (any country known to ever exist) so you can not say something like: Country c = "Foo" because Foo is not a country. I can't imagine having a type for a persons name that holds checks anything but perhaps a strings length, certainly not a list of all possible names. The calendar example I don't get. We already have "date" types in almost all languages so that "works", although it can be used as example of how hard is to implement some types. ----- So I say: >> This approach works good in some cases, but not always And you say: > Does that mean we stringly type everything? Come on now. "Not always" does not mean "Never"
- musingsole 4y ago>A string value is not a great type to convey a user's email address or their country of origin. I can argue whatever type you use in place of a string will similarly be "not a great type". This can be argued in perpetuity because no type/map actually matches the reality it's encapsulating. Type systems require you to build a Pretty Good Theory of your problem space so that your types can overlap with reality/actual usage as much as possible. The problem is at planning/design time, you'll have a Pretty Crappy Theory of your problem space and can only get a Pretty Good one after having wrestled with it for a while. A dynamic, more forgiving language, allows you to build what you can today with the theory you have, and then change it in the future when your theory gets disproven.
- Ygg2 4y ago> I can argue whatever type you use in place of a string will similarly be "not a great type". All Types are wrong, but some are useful. Will Money type solve all problems? Is it better than decimal, from POV of prevention of mistakes - yes.
- deleted 4y ago[deleted]
- croo 4y agoThere is an important difference between types and objects: types are basic building blocks. An email is not a basic building block nor is money. In the mature ecosystem of Java there are libs and standard libraries that handles these problems some are even in the standard library (timestamp with timezone, url...) It would be nice to have stuff for these in the standard libraries but currencies are a moving target and needs constant update to handle the quirks of the real world. For the same reason it's extremely hard to create an "Address" class that handles every possible scenario. In my book the string is a great way to store a country of origin because everything else depends on the usage of that information.
- mejutoco 4y agoIMO the difference between types and objects is that objects have state and behaviour, whereas types only have state.
- grumpyprole 4y agoTo compare them this way is likely to cause confusion. A type (of a term) represents the set of all possible values for a particular term. An "object" in OOP, does not have any formal definition, but is typically a first-class module with mutable state. As such, they can be represented by a term and therefore can have a type.
- mejutoco 4y ago> they can be represented by a term and therefore can have a type. It is informative, I see what you mean. Let me try again following your terms: An object is a type plus behaviour (mutable state).
- grumpyprole 4y agoI am saying that an object has a type, rather than is a type or some augmentation of it. An object is a term-level construction and therefore is not really comparable to a type. Types can be given to both state and behaviour. For example, a function type describes pure behaviour. Note that statically-typed OOP languages have a name for the nominal types of objects: "classes". One could say that a class is a type representing both state and behaviour.
- beachy 4y agoThis "everything old is new again" stuff gets a bit much sometimes. Of course having ability to do strict typing is a good way to avoid bugs and write reliable code. This is not news.
- sanp 4y agoQuestion: isn’t a rich type system as described here similar to OOP?
- masklinn 4y agoThe essay uses a class-oriented type system (they're clearly a .net developer), but the same ideas very much exist in non-OO type systems. And the richest and most expressive type systems are arguably specifically non-OO. Nor are OO languages necessarily statically typed (Smalltalk, Self, Python, Ruby, Javascript, ...)
- Smaug123 4y agoNo no no, entirely not. OOP need not be statically typed at all; see Smalltalk.
- remram 4y agoOr Python, or JavaScript. The reverse is true, you can have static type-checking without OOP, like C, Rust, Haskell.
- tsimionescu 4y agoI feel compelled to note that C only has the lightest possible amount of type checking, if even that. For example, the following program compiles: #include <stdio.h> void foo(double* c) { printf("%g", *c); } void bar() { printf("bar"); } int main() { int x = 9; int* y = &x; foo(x); //warning foo(y); //warning bar(1.0); //not even a warning }
- miskin 4y agoI think it is related to how you organize your data structures and business logic - you may have rich typed model that models specific domain, but OOP would typically call to include all the business logic that modifies attributes of class to be part of that class - eg, you do not externally set specific values to class instance, but instead execute some action on the class instance that may change these attributes. If you use type system just to model data structures and have external business logic to use/change it, it would be called anemic model.
- Barrin92 4y ago>A string value is not a great type to convey a user's email address or their country of origin. These values deserve much richer and dedicated types this is a classic case of not needing more types but needing proper names. Types as concretions, i.e. simply collections of data or functions are a terrible idea because they're static and don't accrete. Data in the real world always does. This becomes very obvious when you go down a paragraph and you see the conundrum: >For example, let's have a second type called VerifiedEmailAddress. If you wish it can even inherit from an EmailAddress. I don't care, but ensure that there is only one place in the code which can yield a new instance of VerifiedEmailAddress okay, and for the next email setup let's have a third type, and a fourth type, and a fifth type, and so on. The end result of this is a zoo of types that help nobody to understand anything. It reminds me of an older Rich Hickey talk. When you program a delivery truck you don't make a type for each different truck because of the contents of the truck, you just take your delivery out of the truck and you don't care about the rest.
- mrkeen 4y agoNo, there should only be the one EmailAddress type. If it's not valid, it's not an EmailAddress. Does having an EmailAddress type guarantee you won't accidentally accept crap? No, but when you get it wrong, you edit the validation in one place in the system.
- ReflectedImage 4y agoIf that place is the EmailAddress type, then you have built your system wrong. You check that stuff when the data enters the system.
- mrkeen 4y agoIf you can construct an EmailAddress, then you have a valid EmailAddress. That's the point. If an EmailAddress can be a valid or invalid email address, then just leave it as a String (since that can also be a valid or invalid email address). > You check that stuff when the data enters the system. Yes > If that place is the EmailAddress type, then you have built your system wrong. No If you validate & construct an EmailAddress from another external class, that means external classes are free to bypass validation and construct an invalid EmailAddress. Putting the validation/construction inside EmailAddress lets you force construction to go via validation.
- djha-skin 4y agoThe most successful languages are typed but weakly so. Just enough type system to avoid the biggest class of bugs, not enough to get in your way all the time. Golang strikes this balance very well. Too little typing, and your Python unit tests get too heavy to run after every commit. Too much, and you have to read a book on category theory before you can figure out how to grab that one field using Lenses in Haskel. Edit: I should note this is coming from an outside observer as my most favorite languages are dynamically typed like Python and Lisp. But it should also be noted that I like writing in small code bases. Larger ones tend to need typing.
- pfdietz 4y agoI want to point out that in practice, Common Lisp is also to some extent statically typed. The compiler will issue warnings if there are forms that it can determine (at compile time) would cause type errors at runtime. It's common practice to not accept code unless these errors are eliminated (one can even set up your compile system to abort when they are found.) What it will not do is reject the program unless it can confirm that every expression will not cause a type error.
- Jtsummers 4y agoThat's dependent on the implementation, SBCL is particularly good at it. Others may just let things pass like: (defun foo () (* 1 "aoeu")) In SBCL gives me this: ; in: DEFUN FOO ; (* 1 "aoeu") ; ; caught WARNING: ; Constant "aoeu" conflicts with its asserted type NUMBER. ; See also: ; The SBCL Manual, Node "Handling of Types" ; ; compilation unit finished ; caught 1 WARNING condition But in CCL it gives me no warnings at all and only triggers an error at runtime.
- tmtvl 4y agoSchemer here, so this question may be silly, but can't you declare or declaim or proclaim or whatever (safety 3) to get stricter typechecking?
- treis 4y agoThe problem with this is the explosion of types when you start with combinations. Stuff like VerifiedEmailOptedOutOfMarketing. I can see a type system designed from the ground up to do something like this. But as a bolt on to existing languages it's pretty clunky.
- throway232lasdf 4y agotype VerifiedEmailOptedOutOfMarketing = { email: string; is_verified: true; is_opted_out: true; };
- recursivedoubts 4y ago> grug very like type systems make programming easier. for grug, type systems most value when grug hit dot on keyboard and list of things grug can do pop up magic. this 90% of value of type system or more to grug > danger abstraction too high, big brain type system code become astral projection of platonic generic turing model of computation into code base. grug confused and agree some level very elegant but also very hard do anything like record number of club inventory for Grug Inc. task at hand https://grugbrain.dev/#grug-on-type-systems https://grugbrain.dev/#grug-on-type-systems
- majjgepolja 4y agoI am grug. More benefit type when there's red line in editor. More benefit type system when F2 rename everywhere.
- cardanome 4y agoOh god, please just use primitive types. Don't make assumptions about things. Everyone thinks they are so smart validating emails, phone numbers, zip codes and all until their great design goes live and they discover that users in the real world do not follow their assumptions. I have seen that happen again and again. No, if your idea of validating an email is more complicated than "should have an @ symbol", I guarantee you, there is counter example that will mess you pretty system up. Have fun scrambling to fix that ticket. Oh you think you know how an address and zip code should look like? No, you don't. Please people, just use strings and call it a day. Why do you like to suffer? I mean, sure, using the type system to protect you from mixing up units can be useful. Everything in moderation. Primitive types are good types.
- revskill 4y agoTo me, type is for data abstraction, like we use generic for function abstraction. Abstraction in this case, mean, for future changes, i just need to change in one place, the Email type abstraction instead of searching and replacing every ussage of primitive email string.
- chii 4y ago> if your idea of validating an email is more complicated than "should have an @ symbol", I guarantee you, there is counter example that will mess you pretty system up. unless you are writing an email server. Then you would need to do this properly. In other words, validate the data that is in the domain of your application. If your app simply _sends_ email, then it's not your domain, and don't need to validate, as long as the receiving end of the email (aka, the email server) accepts it.
- dwheeler 4y agoUsing specialized types can be useful, but if it provide s methods they need to be correct for ALL situations. Here's a subversion of GitHub's authentication (now fixed) where they assumed that "lowercasing domain name using English case rules is always fine and produces the same result" led to a vulnerability: https://dev.to/jagracey/hacking-github-s-auth-with-unicode-s-turkish-dotless-i-460n https://dev.to/jagracey/hacking-github-s-auth-with-unicode-s...
- graypegg 4y agoI’ve always grokked primitive types as mapping to different concepts used when storing values in memory. A string is some bytes in a line with a terminator at the end. An integer is a group of signed or unsigned bytes. An enum value points at another value with a pointer. Etc. What I think this describes is some validation classes, which don’t need to be built into a language’s runtime. Primitive types have a real reason for existing when compiling these apps, a validation class doesn’t. It can just be a library, in which case this is a nice API for validation!
- tsimionescu 4y agoThis doesn't make too much sense. For example, (on 64-bit Linux) long, unsigned long, long long, unsigned long long, double, void*, char*, int(*)(int), []int are all stored in exactly the same way: they are a 64-bit value somewhere in memory. You could argue that double is different since it's just packing a mantissa and exponent, and that signed is actually a sign bit + a 63-byte number, but that still leaves long*, int(*)(int) and long being the same thing: a 64-byte number. Not to mention that struct X { long X } has the same representation as well most likely. Instead, it's more normal to think of types (primitive or not) as descriptions of what can be done with a particular kind of value - from this point of view, it's obvious why long* is a different type than long (you can dereference it) or int(*)(int) (you can't call it). With this new definition, we can also see why we may want to distinguish EmailAddress from String - you can send an email to an EmailAddress, but you can't send an email to a String; conversely, you can sort the characters of a String, but you can't sort the characters of an EmailAddress.
- shaboinkin 4y agoI'm working in a codebase that has, at times, 10+ different expressions within a single conditional in many places, and trying to pull out the context of why the conditional exists in the first place make grug brain hurt. At the very least, you could put all of the expressions and assign to a boolean with a variable name saying wtf it is you're conditioning on. https://grugbrain.dev/#grug-on-expression-complexity https://grugbrain.dev/#grug-on-expression-complexity
- kingdomcome50 4y agoEh... I agree that the minimization of LoC is almost certainly not the most important vector on which to optimize, but I'm not convinced the example linked here is an improvement. The author is obviously correct that their version is easier to debug and slightly easier to understand, neither of these improvements, taken in isolation, satisfy these conditions when taken as a whole. In terms of ease-of-debugging, sure, splattering local variables and extra control statements may allow you to break/inspect a certain class of bug in a certain way. But it also creates a lot of noise and makes the code a lot more "dense". It's hard to see given an example in isolation, but when all of your code looks like this it can make it significantly more "tiring" to understand. "Easy to debug", while important, is also something that must be balanced against other factors. And in terms of easy-to-understand, again, I agree that the author's example has a slight edge (give the first one a shot though... it's not so bad). But what does it mean for a `Contact` to both be "inactive" and also "a family or friend"? They have forgotten to capture the single most important condition! Similar to my first point, it can be hard to see the issue when given an example in isolation, but imagine looking for whatever condition or rule the author is enforcing in a sea of other blocks that look similar. A simple comment over the original version would suffice for me: // If the contact is stale
- docandrew 4y agoI have to plug Ada's rich type system for explicitly encouraging this kind of design. With things like type predicates [1], you can do run-time enforcement or even prove at compile-time (to optimize away the runtime checks) that type constraints are met. As an example of this, in a piece of code I'm working on there's a Base64_String type, where only RFC 4648 characters are permitted to be part of the string, the '=' padding character can only appear at the end of the string, and if the second-to-last padding byte is '=' then the last one must be as well. This is all enforced by the type system without having to call "validate()" or something every time its used. 1. https://learn.adacore.com/courses/intro-to-ada/chapters/contracts.html#predicates https://learn.adacore.com/courses/intro-to-ada/chapters/cont...
- runeks 4y agoThat's definitely cool, but for everyday programming I'd consider this a waste of time.
- pyjarrett 4y agoI agree that it sounds really stupid up front, but it's done when you're just you modeling the constraints of the problem. I've found that it saves a lot of time in debugging and silly mistakes later. For types with invariants, you just add the `Invariant` aspect and then the type invariant gets checked automatically when passed as a parameter. Combined with built-in pre/post conditions, I've found that these sort of automatically inserted checks give me a lot of confidence, and allow significant embedding of conceptual and domain knowledge during development.
- zbentley 4y agoIf you like this style of programming but use Python for your day-to-day, check out typeguard; it provides runtime assertions for parts of the Python type annotation system similar to "Invariant". As with many tools, there are caveats. It's often surprisingly slow (so avoid using it on hot paths, or only turn it on during your testing/pre-production runs) and can't type-check everything (e.g. callables). But it's still pretty nice and requires minimal effort to use! https://pypi.org/project/typeguard/ https://pypi.org/project/typeguard/
- PainfullyNormal 4y agoArticles like this bug me. You've given me a list of why types are awesome. Great. Now, tell me what the tradeoff is. Nothing is free in engineering. To get something, you have to give up something else. Even grug[0] understands this. [0]: https://grugbrain.dev/#grug-on-type-systems https://grugbrain.dev/#grug-on-type-systems
- bigyikes 4y agogrug miss big brain benefit for types. Grug says the main benefit is auto completion, I think the real benefit is to making code changes. If I update a type, the compiler will tell me every single location where I need to make a corresponding code change. For grug: change type give red squiggle, make change code good Also, grug makes a good point about the temptations of generics, but I think they’re exaggerating the impact to the speed of development.
- SaltyBackendGuy 4y ago> big brain type system shaman often say type correctness main point type system, but grug note some big brain type system shaman not often ship code. grug suppose code never shipped is correct, in some sense, but not really what grug mean when say correct I forgot about this, thanks for the morning laugh. No such thing as a free lunch.
- marcus_cemes 4y agoThis is brilliant. Just made my day
- arwhatever 4y agoUnderstanding existing code is a big benefit of static types as well. I’m sure one could argue that member names should obviate the need for type annotations. There’s also the distinct possibility that my preceding ~15 years of statically-typed software development have affected how I think about software development in some way. (wink) But I am finding type annotations internet useful while working on a huge application that is about 2 years into adding a gradual typing system, enough so that I usually take time whenever I enter a new code area to add annotations to everything, just to understand what’s going on. My perception is that I invest time to build understanding of the types, and then document what I’ve learned in the form of these type annotations so that future maintainers then gain a quicker understanding without having to do the initial research. At least my non-statistically-significantly-sized team agrees.
- jmull 4y agoWhy is it better to have two email types, VerifiedEmail and UnverifiedEmail vs. one Email type with an "isVerified" field? One type is probably going to align with storage and transport better, and you probably mostly want to treat verified and unverified email addresses the same except for some very specific situations. (E.g., maybe only your EmailBlaster cares, where it's like a privilege: some can send to unverified emails and some can't.) This seems like a bad for types to me. (It's all code you write and data you design -- types are just one tool... you need to think about they best tool, not get fixated on one, no matter nice it is.)
- blandflakes 4y agoI think you understood the use, but value the safety less than I do: > maybe only your EmailBlaster cares, where it's like a privilege: some can send to unverified emails and some can't A boolean flag is strictly inferior, because it is a runtime check. You can only ever be sure that you're processing verified emails at runtime, and there's no way to require that the code guards against that in all places. If they're different types, you can't even pass an unverified email to code that needs verified emails, so you eliminate the entire possibility at compile-time.
- throway232lasdf 4y ago> Why is it better to have two email types, VerifiedEmail and UnverifiedEmail vs. one Email type with an "isVerified" field? You obviously have no idea what a type is. type VerifiedEmail = { email: string; is_verified: true; }; type UnverifiedEmail = { email: string; is_verified: false; };
- Smaug123 4y agoIn fairness this takes an unusually strong type system to express, doesn't it? Typescript can do it, but I don't think e.g. Haskell98 can do it out of the box in an analogous way? (Of course, it's hard to prove a negative and I'm not super familiar with Haskell, but my evidence is that I'm pretty certain F# can't.)
- 4y ago
- Konohamaru 4y agoYou know those "how to draw Bugs Bunny" art guides they used to include in children's art books? Where they begin with a circle with some guidelines and then do a whole bunch of stuff and the end result is Bugs Bunny? But you have no idea how they went from Point A to Point B? That's the same thing with Type Theory. PROFESSOR: Well, you see, there are different objects, like strings and numbers, that are shaped differently, so we put them into different categories. Those are types! See how simple this is? <a whole bunch of stuff later> PROFESSOR: Now the endofunctor of the covariant types are jointly distributed under the free monoid, provided that the pullback doesn't reverberate the time fibration of the dependent type space.
- xigoi 4y agoIf you actually pay attention to the “a whole bunch of stuff”, maybe you'll understand the latter statement.
- Konohamaru 4y agoThat's not possible, because I can't discern the final cause of all these theorems and definitions in "a whole bunch of stuff". It all comes off as a game of defining abstract objects just for their own sake.
- xigoi 4y agoA good teacher should explain why the definitions and theorems are useful. I'm sorry you've had a bad experience.
- Konohamaru 4y agoI don't know if you're a theist, but you can pray for divine grace that this blockage go away. Other religions like Buddhism also teach the power of positive intentions in releasing spiritual blockage.
- activitypea 4y ago>4 hours ago Ah, you reposted this, hoping the weekend crowd might be kinder. Cheeky lad.
- gnabgib 4y agoBased on the history of dusted submissions[0]... he's a bit of a serial offender [0]: https://news.ycombinator.com/from?site=dusted.codes https://news.ycombinator.com/from?site=dusted.codes
- zbentley 4y agoPerhaps, or perhaps the poster is a beneficiary of the (useful IMO) "do you want to post this again? We felt that it's good but didn't get visibility this time around" moderator outreach tradition.
- yellowapple 4y agoIn my experience with that tradition, I've found that the moderators will just add it to the second-chance queue instead of asking the poster first.
- gary17the 4y agoThis is, IMvHO, such old news that it feels... weird to still read about it in a year with the prefix of 20. Every programmer who has ever single-handedly written a 100,000+ LOC software system will tell you the same thing: shift as much responsibility on the compiler as you can and have the compiler check the code you write to any extent technologically possible. Getting rid of bugs by experiencing, diagnosing and fixing them takes at least ten times more effort than getting rid of bugs by not making them in the first place, through expressing the problem at hand with a strong type system. When you also consider the never ending necessity to introduce change to an already written software system, thus the necessity to refactor code (in the sense of altering the previously assumed meaning of its idioms), the critical advantage of a strong type system becomes self-evident. (Yes, Rust 4ev3r! ;))
- c54 4y agoI agree with this, yet look at some of the extremely salty comments in this thread. People are upset that something might be useful and that they might benefit from learning it or changing their ways.
- tyingq 4y agoSome of the salt might come from experiences using dynamically typed languages that later had some amount of stronger typing added on. No matter how well that's done, it creates friction somewhere in the process interacting with existing code. That is, I can agree that an inherently strongly typed language has benefits, while also being skeptical about bolted on additions.
- c54 4y agoMakes sense. People have been burned. Probably there are lots of people who think about typescript environment setup and source maps when they think about typing, or who think about python's "isinstance(str, foo)". Or who think that it's overly complicated arcane nonsense with weird terminology (lookin at haskell). Or that types specifically refer to borrow checker woes in rust.
- 4y ago
- quechimba 4y agoStarted a Ruby project using Sorbet and I'm refactoring with confidence now. It's such a huge help. The project is about 9000 lines of Ruby by now and I don't think I would have been able to get this far without static type checking
- Jach 4y agoThe second edition of the book Refactoring was written to use JavaScript instead of Java in part to dispel the myth that you can't confidently refactor without static types.
- pipeline_peak 4y agoType systems cause programmers to write 300 classes no one references and many duplicates of each other. Dynamic typing allows you to focus on what really matters, not trivial business logic OOP hierarchies that get inevitably ignored. I feel like in app development, there’s something honest about dynamic typing. You’re focusing on the instance rather than the unnecessary model definition that again, nobody uses and redefined elsewhere anyway. Tbh, I’ve never used a language like JS professionally. I’m sure a lot of code bases are copied and pasted messes with state dependencies and things that make it so you HAVE to focus on the type. It’s an app language, you’re just not gonna find elegance lol. I’ve been a C# dev for a while now. I’ve just found that outside of libraries/frameworks/etc how rarely object model code actually gets reused. And I appreciate the cut to the chase aspect of that. Insert quote about gorilla with banana in forest.
- dgan 4y agoSeems like you mix up classes, and types ... like many others in this thread
- hither_shores 4y agoJava and its consequences have been a disaster for the human race
- pipeline_peak 4y agoI don’t understand, classes are user defined types.
- dgan 4y agothat's the issue: there is no other way to create new types aside from creating a new class in mainstream languages. Those two concepts are separate, and should be treated as such. Types are not Classes, the last is just a lousy "embodiment" of the first
- pipeline_peak 4y ago
- deltasevennine 4y agoTypes got a bad wrap because of C++. There was a strange dichotomy between languages like python/javascript and C++. If type systems were so good why was it easier to program with javascript and python then with C++? People got confused and promoted dynamically typed languages as better. What many people didn't realize was that C++ was hard DESPITE the type system, not because of it. This was soon rectified with type script which eventually caused a complete flip of opinion in the industry once javascript developers realized how much better it is. The other question to this equation is why was python so easy to program for DESPITE not having a type checker (it has external type checks now, but I'm saying before this)? The answer is deterministic errors and easy traceability. If you have an error that happens either at runtime or at compile time you want to easily know what the error is, where it came from, and why it occurred. Python makes it VERY easy to do this. Not all type checkers make this easy (see C++). In actuality type checking is sort of sugar on top of it all imo. Rust is great. But really the key factor to make programming more productive is traceability. Type checking, while good is not the key factor here. Think about it. Whether the error occurs at runtime or compile time is besides the point. Compile time adds a bit of additional safety, but really if an error exists, it will usually trigger at some point anyways. The thing that is important is that when this error occurs whether compile time or runtime you need as much information about it as possible. That is the key differentiator. That is why typeless python and typed rust, despite being opposites, are relatively easy to write complex code for when compared to something like C++.
- kaashif 4y ago> Whether the error occurs at runtime or compile time is besides the point. Compile time adds a bit of additional safety, but really if an error exists, it will usually trigger at some point anyways. Well, if the error is at compile time, there's no chance that code makes it to production and affects customers. If the error is at runtime, you need to have tested that edge case and if you haven't, there could be customer impact. I mean, once you see a few TypeErrors in Python code with no type annotations, or a few NullPointerExceptions in Java where there's no compile time null checking by default, I think it becomes very clear that catching things at compile time is much better...
- dustedcodes 4y agoHey author here! Sorry I didn't respond to any feedback yet. I've literally posted this before leaving my house and didn't think it would get many upvotes as it didn't get any votes the other day either. Sorry that the general sentiment is "everything old gets new again". I didn't try to rehash some old news again. I basically blog about things that come up in my daily work life and this topic was something that I felt quite passionately about. From my own experience I felt that type systems, especially in modern languages, are not nearly as well utilised as they could be. Of course there is always a balance to strike, especially with over engineering and needless optimisations, but that is a topic for another blog post another day.
- zbentley 4y agoDon't let it get you down. HN sentiment often trends grumpy when someone makes a point that's been made before. That doesn't mean it's not important to restate, extend, elaborate on, modernize, and recontextualize ideas! There are almost 8 billion people on this planet; most claims echo prior statements to some degree. I found your article practical, short, and largely accurate; which is to say: I liked it. I think it could be improved with either an edit or followup which links to similarly-inclined articles, papers, or talks that discuss the topic, so folks can deepen their understanding of the role of type systems in day-to-day programming and PLT.
- abraxas 4y agoI'm learning Python after 35 years of working with statically typed languages (Pascal, C++, Java, a bit of Typescript lately) and by god this is hard. Not because there is anything in the language that I don't understand but the lack of any type info is killing me. I just can't build up a rhythm of coding. I feel like every five lines I have to sprinkle in print() statements to keep track of the data transformations as there is nothing useful that can be captured about it even with these weak ass "type hints". I know that even when I get this crap to work I'll hate going back to that code in a few months as I'll have forgotten what the hell it all did and will have to spike it with print() ad df.shape() again to make sense of it. And naming discipline can only get you so far. Maybe dynamic typing just isn't my thing and I need a new gig...
- _dain_ 4y agouse mypy? it can enforce the type hints.
- maxbond 4y agoMyPy is awesome and will make you a more productive programmer and will make your application more robust. But I agree that Python types are "weak ass". It's not a particularly ergonomic type system to use, and it's more difficult to express complex types than is worth it for the sometimes questionable benefit. 3.10 does add unions using | which is nice, and I expect the type system will get better, but I share these frustrations.
- maxbond 4y agoI've moved from Python to a static language and it's made programming enjoyable again. I'd forgotten that that was possible. It's much more productive as well. Using Python as a production application language is like playing operation. For me what I really detest is ambient, untyped (in the sense that they aren't declared in the function definition) exceptions. Exceptions can just happen on any line, and there's no way to know what exceptions a function will raise. So you have to dig into the source code of your dependencies and such, it's a tremendous waste of time and you still get unanticipated exceptions in production.
- yellowapple 4y ago> I want that data type to have helpful methods such as .Domain() or .NonAliasValue() which would return gmail.com and foo@gmail.com respectively for an input of foo+bar@gmail.com. No the hell you don't. Please please please do not attempt to separate the alias from an email address I submit. It's there for a reason - specifically, to hold you accountable if I experience a sudden influx of spam, and generally to keep things categorized in a world where senders can be sending things from all sorts of domains. Knowing that this is something one would even remotely consider is grounds to never touch anything one has built with a ten-foot pole, and I am now very strongly inclined to look into the author and compulsively scrub any accounts of mine from anything said author might've touched. I am not exaggerating. The thing before the @ is meant to be opaque. Deeming otherwise for the sake of something so blatantly user-hostile as removing aliases is plain evil, and I will not sugarcoat my condemnation of such practices. If you're sufficiently sociopathic to have no regard for the morality argument here, then at the very least take heed of RFC 5322 (https://datatracker.ietf.org/doc/html/rfc5322 https://datatracker.ietf.org/doc/html/rfc5322) and recognize that trying to parse any meaning from an email address' local-part is blatantly ignorant of IETF specifications and almost certainly will create bugs. Just don't do it - if not for your users' sake, then for your own.
- pencilguin 4y agoTrue enough, as far as it goes. But if you are concerned about subscribing to something twice, you may want to try to check delivery uniqueness. They might be your own addresses. Of more interest to me, omitted from the presentation--as almost always--is anything about what is disliked about a malformed address. You see this when some web form says it doesn't like your address, but won't say why, leaving you to guess and try things until it is satisfied. Another example is the password filter that idiotically demands "at least one capital letter, one digit, and one swear character" in your already several-word passphrase, and dislikes your choice of swear characters but won't say so.
- xigoi 4y ago> But if you are concerned about sending an e-mail to the same address twice, you need to check delivery uniqueness. For one, you shouldn't be concerned about that, and for two, you can't tell delivery uniqueness anyway, since someone can have multiple completely different addresses going to the same inbox.
- LeicaLatte 4y agoSwift does this exceptionally well and always has your back.
- whiddershins 4y agoAt the opening of the article, as the author describes a type, it ends up sounding like a class to me.
- snickerer 4y agoI summarize the article as "use OOP and design your classes well".
- pencilguin 4y agoHas absolutely nothing whatever to do with OOP. OOP, where it means anything at all, involves runtime selection of operations according to types organized in hierarchies. TFA is about compile-time type compatibility enforcement.
- GnarfGnarf 4y agoAu contraire, types can be implemented with OO classes and operator overloading. Errors caught at compile time.
- pencilguin 4y agoClasses can be OO. Or not OO. Makes no difference, at compile time.
- snickerer 4y agoSorry, I was not clear enough. I come from the C++ world, where it is natural that types are checked at compile time. And where you would use classes to implement the 'sophisticated types' the author suggested. So in the ears of a C++ programmer the article says: design your classes well.
- pencilguin 4y ago"Lean hard on your type system's capabilities".
- GnarfGnarf 4y ago100% agree. I love C++ for allowing me to do things like: CInches in1, in2, in3; // basically just floating points CCm cm1; // basically just floating points in1 = in2 + in3; // no compilation error in1 = in2 + cm1; // compilation error in1 = in2 * in3; // compilation error in1 = 2. * in2; // no compilation error float foo(CInches *); foo(cm1); // compilation error Obviously there's a lot of code behind "CInches", but it catches so many errors. Which other languages also support this?
- xigoi 4y ago> Which other languages also support this? Haskell and related languages have distinct types; so does Nim. But even if there's no direct language support, you can always emulate them by having a compound type (record, struct, class, whatever you call it) with one field.
- kazinator 4y ago> A string value is not a great type to convey a user's email address or their country of origin. These values deserve much richer and dedicated types. I want a data type called EmailAddress which cannot be null. Sure, I'm on board: I also want an e-mail address type. Just not in your shit language in which something can be of type String, yet be null reference.
- sixstringtheory 4y agoAre there any widely used statically typed languages where there is no such thing as null? Wish I could find a job using one of those!
- kazinator 4y agoHmm, oh! C++ comes to mind. Of course, there is such a thing as null, but in: void fun(string x) // std::string { } x cannot be null. The reference semantics (like a copy of a string sharing the data with the original) is an implementation detail/optimization encapsulated inside what appears to be a value type. The tools are there in C++ to create ideal types for your problem domain which just look like value types that have no null domain value (or any other value you don't want), and standard strings are like this.
- sixstringtheory 4y agoYou’ve moved the goal posts, but seem to have forgotten that you can create pointers to C++ strings, which then can of course be null.
- kazinator 4y agoPointers to std::string are almost never required, though. A pointer to std::string is not something you have to use to write a string handling C++ program or module; and such a pointer p is itself not a string, *p is (if p is non-null and valid). About the only time you would need a pointer to std::string when calling some C API that takes a callback function with a void * context, and you'd like that context to be a std::string. Then you might take the address of string object to pass to that API. (That pointer would likely never be null, but could go bad due to lifetime mismanagement.) Most other uses of such a thing would be unidiomatic. Whereas, in some languages, string references that can be null are foisted on programmers as the standard, idiomatic string representation. That's a big difference.
- MagicMoonlight 4y agoAnd that's why go is a meme language. It replaces clear statements like "int x = 0;" With "var x = 0;" as if that's somehow better. So instead of having clear blocks of types that you can visibly read, it's concealed. And the type could change each time you run the compiler.