[HN Gopher] Things I Was Wrong About: Types
       ___________________________________________________________________
        
       Things I Was Wrong About: Types
        
       Author : abyx
       Score  : 161 points
       Date   : 2020-09-27 07:37 UTC (8 hours ago)
        
 (HTM) web link (v5.chriskrycho.com)
 (TXT) w3m dump (v5.chriskrycho.com)
        
       | mantap wrote:
       | A good static type system is better than dynamic typing. But,
       | dynamic typing is better than a bad static type system.
       | 
       | A bad static type system will slow you down, forcing you to
       | appease its irrelevant complaints, and pervert your code into a
       | form that no sane programmer would choose to write it in, if it
       | weren't for the type system looking over their shoulder. I won't
       | mention any names, but suffice to say such languages exist.
       | 
       | On the other hand, a good type system recognises that its goal is
       | to allow the programmer to write code first in the form that
       | makes sense to them, and _then_ be expressive enough to describe
       | it.
        
         | igouy wrote:
         | good is better than bad !
        
       | donatj wrote:
       | I hated types in college, I thought they were a huge waste of
       | time, and on their way out.
       | 
       | But over the years that opinion shifted. I got experience with
       | the actual types of problems we would see. Again and again
       | runtime errors in production, usually from type issues. This
       | really pushed me to invest my time into static analysis tools,
       | and static analysis tools work best when types are at the very
       | least annotated. This was an lead in to compiled typed languages
       | where these types of runtime errors are rare if not impossible.
       | 
       | As someone in a similar boat, I feel still like it's more fun to
       | knock out a really quick prototype in an untyped language.
       | 
       | For something I actually have to maintain and build tests for, a
       | well typed language is absolutely preferable. I used to quip that
       | no one could build maintainable JavaScript, and I enjoyed writing
       | JavaScript, but now with TypeScript I think it's largely doable.
       | 
       | I think what really changed in me is my desire to knock something
       | out quickly was replaced with the desire to have stable software
       | where components could be built well from the get-go and not need
       | modifications for years.
        
       | Toutouxc wrote:
       | Is 'Maybe Haskell', mentioned in the article, worth reading? Or
       | can anyone recommend a similar book of the 'just enough to be
       | dangerous' kind on Haskell or maybe Clojure?
        
         | christoomey wrote:
         | 'Maybe Haskell' is a fantastic intro to using types and why you
         | might care about them. It's focused in scope and really meant
         | to give you a taste of some functional ideas and a bit of
         | Haskell, and in that effort it does a great job.
         | 
         | Also, you can't beat the price ($0). If nothing else, probably
         | worth browsing through to see if something resonates with you.
        
         | filoeleven wrote:
         | I learned Clojure by reading _Clojure for the Brave and True_
         | and can recommend it as a good intro. It helps that the
         | language itself is simple and consistent. You can get
         | remarkably dangerous with only a few hours of study.
         | 
         | https://www.braveclojure.com/
        
         | tom_mellior wrote:
         | I don't know about Maybe Haskell, but you can give Real World
         | Haskell a try for free: http://book.realworldhaskell.org/
         | 
         | I found it a pretty good introduction. Unfortunately at some
         | point it devolves into typical Haskell "how can we turn this
         | clear, readable three-line function into a _two_ -line function
         | by peppering it with obscure operators".
        
       | jgilias wrote:
       | I once told a JavaScript guru colleague of mine that I was
       | spending my free time dabbling in Haskell. His response was 'lol,
       | why would you do that?'. His point was that spending time
       | learning things that you're not going to be using directly any
       | time soon is a waste of time. My point was (and still is), that
       | learning such things opens up a completely new way of thinking
       | about problems and potential solutions.
        
         | quickthrower2 wrote:
         | Does he use Redux by any chance :-)
        
         | mumblemumble wrote:
         | Limiting what you learn is, by definition, self-limiting.
         | 
         | On the one hand, that's a useless truism - we can't learn
         | everything, so you do have to pick and choose. On the other
         | hand, I still find the framing useful, because it reminds me
         | that, in a sense, the decisions about what _not_ to learn are
         | more impactful.
         | 
         | To the example of programming languages: Only focusing on
         | perfecting my skills at the tools I currently use would leave
         | me less able to understand the limitations of the tools I
         | currently use. And would limit my ability to take my career in
         | new paths where I might have a need to use other tools. On the
         | other hand, learning every single new thing might distract me
         | from properly mastering my current tools, or might eat up so
         | much of my free time that I'm effectively spending all my time
         | thinking about work and never recharging my batteries.
        
         | wccrawford wrote:
         | My experience matches yours. Every time I learned a new
         | language, I gained a new understanding of a different way of
         | doing things, and I was able to bring some of that
         | understanding to my daily job's language. It's absolutely made
         | me a better programmer.
         | 
         | I didn't just study those languages for nothing, though. I
         | always had a purpose in mind for them, with the possible
         | exception of Ruby which just seemed neat.
        
         | loup-vaillant wrote:
         | _" Never learn anything you don't see immediate use of"._
         | 
         | Such short term thinking could explain why dynamic typing is so
         | appealing: simpler implementations, more possibilities than
         | most mainstream static type systems, while requiring basically
         | no learning at all.
        
       | jariel wrote:
       | People often think about coding, but that's generally a small
       | piece of it.
       | 
       | Typing is a form of structure, and especially a kind of
       | documentation.
       | 
       | When encountering APIs of various kinds 'typing' is part of how
       | they are expressed.
       | 
       | It's hard to build large monoliths without typing.
       | 
       | When working with 'wide scope' problems, when the typing is more
       | oriented towards the data, that's another thing altogether, which
       | is why I think for many systems untyped JS and Python works well
       | enough there.
       | 
       | What most typed systems lack is a fluent way of mapping to 'data
       | types' which are often external to the system, or at least
       | externally defined.
        
       | carapace wrote:
       | Thesis, Antithesis, Synthesis
       | 
       | Types Bad, Types Good, Types are a gateway into sophisticated
       | automation of programming tasks but not a panacea.
        
       | hn_throwaway_99 wrote:
       | I'll add a 4th thing to his list based on my experience moving
       | from Java to Typescript:
       | 
       |  _Nominally_ -based type systems like Java (where you can only
       | write Foo f = new Bar() if Bar has Foo somewhere up it's static
       | type chain or interface hierarchy) are way more of a pain in the
       | ass than _structurally_ -based type systems like TypeScript
       | (where you can say f: Foo = new Bar() as long as TypeScript
       | determines Bar has all the required fields of Foo).
       | 
       | Primarily this makes refactoring _much_ easier because you don 't
       | have to necessarily change things up a (potentially brittle) type
       | hierarchy. Instead, you can just add new properties to your type
       | to fulfill the set of properties required on the target type.
        
         | couchand wrote:
         | That isn't always a benefit, though. When interfacing with a
         | database schema that uses small integers for ids, I'm all the
         | time defining types like `struct CustomerId(u32)` and `struct
         | InvoiceId(u32)`. It's incredibly valuable that the compiler
         | will not let me mix up a customer and an invoice, even if both
         | of them are 42.
        
         | tasogare wrote:
         | This is why interfaces exist.
        
       | lolive wrote:
       | Strings, integer and boolean are enough to model the world.
       | Period. Ps: oh, and 640k should be enough for everyone!
        
       | kobe_bryant wrote:
       | I'm working on a system where the users supply equations as rules
       | and in a roundabout way we came across static vs dynamic typing.
       | 
       | having users have to define each equation and its type (boolean,
       | numerical, etc) would make it simple and foolproof, but add extra
       | work and requires defining each equation separately when a lot of
       | them are just partitioning the space (imagine 30 combinations of
       | x, y and z) so we ended up deriving the type from how its result
       | is being used, but that puts the work of getting it right on the
       | user
        
       | rogerkirkness wrote:
       | Rewriting our app from Node to Go reduced cloud spend by 20x, did
       | not increase dev time, made it much easier to read and grow the
       | project past 5k lines. Literally no downside.
       | 
       | If your database is typed on the bottom, and your documentation
       | is typed on the top, you have a type sandwich in every non-script
       | application. Might as well be consistent.
        
       | raverbashing wrote:
       | Yeah I agree most of the pain of types comes from the ways
       | they're implemented (especially in early C++/Java etc)
       | 
       | Hence my beef with them. PyLint (as an example) can deduce and
       | type check your program for you. Why do I need to annotate the
       | types for every single thing? Python is not exactly a weakly
       | typed language if you go down the details.
       | 
       | So if the compiler knows (or even worse in the case of Java: the
       | IDE knows), why do I need to tell it that? Then you end up with
       | the cases of several prototypes for the same function for things
       | that are essentially the same.
       | 
       | I can understand type annotations for documenting interfaces.
       | Those make sense.
        
       | wutbrodo wrote:
       | I which there was a little more elaboration on _why_ he
       | originally disliked types so much. This perspective is still very
       | strange to me, as the value of types seems self-evident for
       | systems more complex than a script, and I'd like to understand it
       | better. As it stands now, my only assumption is that this comes
       | from the place the author is coming from: people who don't think
       | types are useful are generally coming from a place of ignorance.
       | 
       | This feels facile and self-serving though ("people who disagree
       | with me don't know what they're talking about"), so it's more
       | likely that I have a blind spot. Are there any current or former
       | type-haters that can help shed light on their perspective?
       | 
       | EDIT: I should note that my production experience is primarily
       | with (modern)C++ and Python, so I don't even have the benefit of
       | more modern static typing systems like Rust's.
        
         | edanm wrote:
         | I originally worked with C++. I then moved to Python and loved
         | it, partially because of the dynamic type system. I'm still in
         | the dynamic-types camp, though I haven't really learned a
         | language with a good static type system yet, and the little
         | I've learned of Haskell has made me super interested in a good
         | type system.
         | 
         | To answer your question, the reason I prefer Python to C++, in
         | terms of types at least, is that I feel the type system makes
         | you get the order of building a program wrong. I think the
         | right way, in most cases, is an agile-style, prototype-first
         | approach. You should usually start with the simplest prototype
         | of something that does what you want, then slowly expand and
         | generalize as you learn more about the problem domain and make
         | product and design decisions.
         | 
         | With C++, you usually start off with lots of decisions on how
         | your data looks, and it's usually really hard to make changes
         | afterwards. This forces you to make a _lot_ off the
         | architectural decisions when you know the least, and are fairly
         | locked into those decisions.
         | 
         | The main benefit proposed benefit of a type system, or at least
         | of C++'s type system, is that the compiler can help you catch
         | type errors. However, these errors represent a small fraction
         | of possible buga - so you're going to have to write tests
         | anyway, and it's not hard to add type tests as well (where
         | relevant- the idea is that it's often less relevant than you
         | think!).
         | 
         | Caveats to my opinion:
         | 
         | 1. I haven't worked in C++ for many years, and I know that
         | there's been a lot of changes. Not sure how my opinion stacks
         | up to current cpp.
         | 
         | 2. If the type system gives you more than just catching some
         | class of errors, I can see that it might be worth it. Again, no
         | real experience with anything other than Python and js in many
         | years.
         | 
         | 3. I'm pretty sure I'm right about the correct way to build
         | software in terms of steps. The older waterfall approach where
         | everything is designed to front makes little sense in most
         | projects I've seen, or at least in the early phases. Possibly
         | something like Excel, in exchange there's very little new
         | product development and in which the product domain is super
         | well understood, has different trade offs.
         | 
         | Having said that, I might be wrong about how much cpp forces
         | you to do things the other way. That was my experience and most
         | people's that I know- but that was also the common way to do
         | things in the past anyway. Possibly, there are good ways of
         | doing that kind of development in cpp today.
        
         | steveklabnik wrote:
         | I can't speak for Chris, but I can speak for myself.
         | 
         | I did the bulk of my early programming in statically typed
         | languages. Specifically, late 90s C, C++, and Java. Then I
         | found Perl, and pretty much went into dynamically typed
         | languages only for the next near-decade.
         | 
         | At the time, I felt like the types didn't pull their weight.
         | There was a _lot_ of extra writing, and you got very little
         | benefit for it. Plus, dynamically typed languages had these
         | rich features that just weren 't really accessible in
         | mainstream statically typed languages, and so they kind of
         | became associated with each other in my brain even if that
         | wasn't specifically true. For example, seeing stuff like
         | https://www.cs.ait.ac.th/~on/O/oreilly/perl/cookbook/ch11_08...
         | absolutely blew my mind, and I had no idea how you could do
         | something like this in the statically typed langauges I was
         | exposed to at the time.
         | 
         | It wasn't until I discovered languages that had significantly
         | more powerful features, and had a significant amount of type
         | inference, that I felt the equation changed. The former to give
         | me something better than purely "don't make some kinds of
         | simple mistakes", and the latter to make it ergonomic enough.
        
           | wutbrodo wrote:
           | > Plus, dynamically typed languages had these rich features
           | that just weren't really accessible in mainstream statically
           | typed languages, and so they kind of became associated with
           | each other in my brain even if that wasn't specifically true.
           | 
           | This resonates a lot with me (though as you say, isn't really
           | a typing thing). I work on ML systems for autonomous
           | vehicles, so I switch between Python and C++, and I
           | definitely occasionally start writing some C++ logic in an
           | elegant functional way and realize that C++'s boilerplate
           | makes it less readable than the usually-less-readable naive
           | construct like a loop (eg I'm surprised at how often
           | std::transform comes out looking terrible).
           | 
           | Do you mind if I ask about the size of the systems/teams
           | you've worked on throughout this process? My experience with
           | C++ has been in large systems worked on asynchronously by
           | lots of engineers (with strong code review policies in
           | place), and my experience with Python has been everything
           | from moderately-sized systems down to smaller ones down to
           | scripting. The Python case also included work with an
           | inexperienced (and frankly, partially unintelligent) team of
           | engineers, which made the discipline imposed by typing all
           | the more valuable, but I have found the documentation and
           | structure that typing imposes on code to be invaluable in
           | communicating
           | 
           | As I said in my other comment, I'm still concerned this is a
           | little facile, but having started my career on C++ systems at
           | Google, I wonder whether I've just set a standard so high for
           | the health of a system that many of those who dismiss
           | typing's value don't understand that those benefits are
           | possible (I'm certainly an order of magnitude better at
           | reading code than anyone my current team, but this is
           | confounded by the fact that everyone else has a weaker
           | engineering background and stronger robotics or ML domain
           | expertise).
           | 
           | To be clear, I don't think this applies to you necessarily;
           | your approach of trading off the benefits of ergonomics vs
           | structure resonates strongly with me, and I'd imagine it's
           | even more the case on pre-modern statically-typed languages.
           | What I'm really curious about is the perspective expressed in
           | the OP, which, in the 2010s, doesn't even see _value_ in
           | types beyond perhaps negligible benefit from some compiler
           | checks.
           | 
           | Thank you very much for the perspective!
        
       | edem wrote:
       | I've been using Kotlin (which is __very __similar to Typescript,
       | the language the author also mentions) to great effect mostly
       | because 2 of those points (type inference and sum types) and
       | something else that 's also __very __powerful and not mentioned
       | in the article: extension functions.
        
       | jondubois wrote:
       | For me it was the opposite. I started out with dynamically typed,
       | then switched to statically typed for many years, then I realized
       | that types were almost worthless (and even harmful in some cases
       | in terms of how they influence design/architecture) and I
       | switched back to dynamically typed and never looked back.
       | 
       | Nothing beats good testing and good architecture design.
        
       | choeger wrote:
       | For me it is somewhat different: I always used typed languages
       | and occasionally have to read and improve untyped code. I must
       | admit, I feel half blind in the latter. When debugging a python
       | web service, trying to figure out the control or data flow, I
       | very often scratch my head and wonder "what _is_ this thing "?
       | This applies both to library functions and code from colleagues.
       | Sometimes it feels like untyped languages are write-only
       | languages.
        
         | christophilus wrote:
         | This is my biggest gripe, too. Discipline about commenting
         | helps, but only so much.
         | 
         | In my case, I have Clojure spec or the JavaScript equivalents
         | at the important boundaries in my code. That makes this issue
         | much less painful and also solves a few other issues to boot.
         | 
         | To be honest, in more advanced type systems, I'd ask: "What is
         | this" only to be shown a convoluted type signature. I then put
         | a println or breakpoint in there just like I would with a
         | dynamic language and poked around.
        
       | JackMorgan wrote:
       | Types are controversial because we can't measure engineer
       | productivity. Full stop. I see people on here arguing that they
       | are faster one way or the other, yet we have absolutely no
       | empirical evidence for such a claim. What makes it more complex
       | is that "fighting with types" often takes one out of the flow in
       | a different way than usual progamming challenges. Since it's
       | unmeasurable, we cannot see the effect of productivity relative
       | to team size, feature churn, product timeline, unit test
       | discipline, etc.
       | 
       | For example, I'm completely convinced types add significant
       | productivity increase to any team of more than two programmers.
       | Likewise types are a net productivity improvement to software
       | that has to be added to multiple times in a year. If it's a one-
       | off write and throw away, or tiny team, it probably doesn't help
       | as much. Also if it's a big team, but with highly siloed
       | developers, likely doesn't add as much value. I think fighting
       | with types FEELS slower than it is, like how dealing with a car
       | that doesn't start immediately or a street with a lot of stop
       | signs feels slower than just getting out and walking, despite the
       | empirical evidence to the contrary.
       | 
       | However, these beliefs of mine are BACKED BY ZERO EVIDENCE. Just
       | let that sink in. We all are arguing about a topic that
       | inherently cannot be measured. We should be trying to tackle the
       | measurement issue first, rather than just keep yelling about
       | chocolate vs vanilla forever.
       | 
       | When all we have to go on is feelings, we get people arguing
       | which is better: cars or bicycles, without any discussion or
       | facts about top speed or total distance. The cyclist is always
       | going to talk about wind in their hair feeling like they are
       | going so fast, or how it slows them down to look for gas stations
       | and just stand there pumping gas when they could be making
       | progress pedaling. And to stretch the metaphor even further,
       | there are plenty of environments when a sedan is slower than a
       | mountain bike, and plenty where they are the same. I suspect all
       | this is true with types, yet we have absolutely no way to know.
       | For all we know, the "collective wisdom" of types might be
       | exactly backwards, because basing engineering decisions on
       | feelings rarely correlates to empirical results.
        
         | adnzzzzZ wrote:
         | I personally believe that productivity differences depend
         | mostly on the programmer's personality. Certain types of
         | personalities will be more productive with statically typed
         | systems, others with more dynamically typed ones. If this is
         | the case then the measurement problem becomes one of
         | personality assessment rather than anything else, and even when
         | we manage to get this measured correctly, because it's a
         | personality issue, we'll still have people arguing cars or
         | bicycles without facts being able to help at all (except to
         | point that it's mostly a matter of preference so the discussion
         | is pointless).
        
         | fulafel wrote:
         | I think from an empirical mindset we have to grant that the
         | lack of evidence probably means there is no significant
         | advantage. Because there have been lots of studies, we can't
         | simply say we don't know if there's an effect, or we don't know
         | how to measure it.
        
           | JackMorgan wrote:
           | Don't conflate the inability to measure productivity with the
           | conclusion that for this one issue the differences can't be
           | that large. That's like saying, "just because we can't
           | measure distance or velocity, rockets and cars probably are
           | similar speeds, or it depends on the driver's personality."
           | It's empirically unsolved how productive an engineer even is,
           | therefore all other discussions are 100% subjective.
           | 
           | We don't know if there's an effect, because we have no way at
           | all to measure it without costs so staggeringly large no one
           | will ever try. We'd need similar engineers, doing all the
           | same practices, with the same stories, and just one
           | difference. But then the engineers would need to have been
           | selected to be similar skill and speed previously, which
           | would require doing other projects with all fixed practices
           | and languages to measure developer skill relative to peers.
           | This also doesn't take into account team cooperation and
           | morale, creative thinking around problem solving, etc. It's a
           | bummer, but without some breakthrough in AI research allowing
           | developers to be measured based on the code they write, it's
           | seeming more and more like an unsolvable problem.
        
       | dynamite-ready wrote:
       | Types are useful, but they are currently trending, so now, they
       | might often be forced into situations where they might not be
       | needed.
       | 
       | Programming languages and their type systems are tools, at the
       | end of the day.
       | 
       | Occasionally, an overwrought system of types will slow you down,
       | or quite possibly make simple changes impossible.
       | 
       | On another day, some other type declaration could save you hours
       | of debugging, or speed up your program by orders of magnitude...
       | 
       | Simply put, I feel that many Java programs probably could happily
       | be replaced by Python or Node JS.
       | 
       | On the other hand, some safety critical UI projects (or at least,
       | some key portions of them) absolutely would benefit from
       | Typescript or Elm.
       | 
       | I wish there was more discussion about when each is a better fit,
       | rather than talking about how much worse A is than B.
        
         | jondubois wrote:
         | Type hype is real. It almost seems like job-creation propaganda
         | at this stage.
         | 
         | When I judge things, I look at practical outcomes; and the fact
         | is that I produce better software with more features within the
         | same timeframe if I use JavaScript rather than TypeScript and
         | the product in both cases is equally robust. This has been true
         | for me both independently and as part of a team.
         | 
         | With JS, I can write more code and more tests within the same
         | amount of time and there is no drop in quality.
         | 
         | I'm very surprised that nobody else seems to be experiencing
         | the same thing. I've been back and forth many times between the
         | two paradigms and for me it's clear as day.
        
           | valuearb wrote:
           | I switched my Javascript code base to Typescript a few months
           | ago. There is a productivity cost that is declining over time
           | as I get more used to Typescript. But the conversion also
           | flushed out some significant bugs in the JavaScript base.
           | 
           | And I only get to work on this code once a week. So when I
           | come back to it I've found it's much clearer how it works and
           | I get productive much faster.
           | 
           | I will bet any coworkers who have to work on your code wish
           | it was in Typescript.
        
           | rswail wrote:
           | Sorry anecdata isn't data. You might be able to write more
           | code in terms of LOC but you're also writing tests that a
           | strongly typed system wouldn't need.
           | 
           | You don't _know_ that your code is  "equally robust". You
           | don't know what sort of "drop in quality" you have because
           | you're not using strong types.
           | 
           | You are making a judgement that isn't backed by anything
           | other than intuition.
        
         | rswail wrote:
         | The focus of industrial development for a long time has been
         | focussed on the processes, or the verbs, not the things, or the
         | nouns.
         | 
         | Focusing on the Nouns and the types of those Nouns lets you
         | understand what states they can be in and more importantly, the
         | ones they cannot be in. By using types as much as possible to
         | express those invariants and constraints, you can make sure
         | that the processes don't do something that they shouldn't.
         | 
         | Proving software correct is a very complicated and, as yet,
         | unsolved in most practical cases. Strong typing and strong type
         | systems are a step in that direction.
         | 
         | If a compiler ensures that a sum type's possible values are
         | always exhaustively matched, then you can be sure that the
         | processing at least _considers_ all of the possible values.
         | 
         | That leads to positive results when programming.
         | 
         | If you use Option types and then ensure that the None type is
         | handled, the possiblity of nulls is dramatically reduced, if
         | not eliminated.
         | 
         | If immutability is enforced, that removes the possibility of
         | inadvertent modification, especially somewhere deep in a call
         | stack. That leads to safer concurrency, which is an absolute
         | requirement for using the compute capacity to its fullest
         | extent.
         | 
         | Strong typing like this isn't "overwrought", it's making sure
         | that your code doesn't do something to a thing that it
         | shouldn't do.
        
       | fizixer wrote:
       | I was wrong about types too. I used to think after I had mastered
       | half a dozen languages, I should start spending time on typed
       | languages, maybe eventually learning type theory, Haskell and
       | what not.
       | 
       | Now I focus on things that matter: deep learning, AI, and
       | robotics.
        
       | chousuke wrote:
       | For my use cases, the vast majority of errors with dynamic
       | languages boil down to being able to run scripts that have
       | undefined variables; I'm prone to (mental) typos, so if I had a
       | dialect of Python that failed before runtime when undeclared
       | references exist, I could probably cut the number of iterations I
       | need to arrive at a working script by at least half.
       | 
       | I've never found a valid use case for allowing undefined
       | references, though I suppose it does make the language
       | implementation significantly easier.
       | 
       | Writing tests is not really a solution either. Tests are also
       | code and suffer from the same problem.
        
         | christophilus wrote:
         | At least in JavaScript, these are flagged by a linter. I
         | imagine Python has a decent linter and code editor integration
         | of said linter?
        
         | petters wrote:
         | pyflakes finds these. It has virtually 0% false positives and
         | runs extremely quickly.
         | 
         | Run it on save or in a git commit hook.
         | 
         | (I agree that they should be a syntax error though)
        
           | samanator wrote:
           | Would be very difficult (or impossible) to make this a syntax
           | error since
           | 
           | exec()
           | 
           | Modifies the global scope
        
         | samanator wrote:
         | Pycharm warns you when that happens. I'm guessing they are
         | using a combination of the abc module (to parse the abstract
         | syntax tree) and some linter that can be found on pypi.
         | 
         | Would a validator that runs before your main python executable
         | solve your problem?
        
           | samanator wrote:
           | Oops I meant the ast module, not the abc module
        
         | knocte wrote:
         | Just make the jump for once:
         | https://github.com/knocte/2fsharp/blob/master/python2fsharp....
         | (yes you can write scripts with this language).
        
           | chousuke wrote:
           | I most often choose Python because it's what's available, and
           | it's good enough for the things I need to use it for. Other
           | people can generally understand my code, too.
           | 
           | F# probably is a good language, but I haven't had a good
           | enough reason to use it.
        
       | diminish wrote:
       | 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 one
       | approach `integer`/`float`/`string`/`datetime`.
       | 
       | - Each type being well defined and each object-class being used
       | as type is another strict approach.
       | 
       | Look at the types of Rust and Kotlin and see the cacophony of
       | decisions made:
       | 
       | - Rust https://doc.rust-lang.org/reference/types.html
       | 
       | - Kotlin https://kotlinlang.org/docs/reference/basic-types.html
       | 
       | 2. When you want to implement a method which applies to multiple
       | libraries, you end up writing `fooString`, `fooInteger`,
       | `fooDatetime` etc (or foo(String), foo(Integer)) etc. DRYing is
       | too hard. So you end up writing 10x more methods
       | 
       | 3. No you don't avoid `null` checks at all.
       | 
       | 4. Generalizations are harder to implement.
       | 
       | 5. Interfaces are uglier compared to dynamic languages.
       | 
       | 6. The code becomes less readable, not concise and verbose
       | 
       | 7. Every new and old typed language is less elegant compared to
       | the dynamic. Look at typescript vs javascript, typescript code is
       | ugly, verbose, non-readable at all.
        
         | inertiatic wrote:
         | >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 implement, causing you
         | to think harder about what should be allowed under these
         | generalizations, leading to less bugs, leading to things
         | being... easier to actually implement in the long run.
         | 
         | >6. The code becomes less readable, not concise and verbose.
         | 
         | Since you only recently got into dynamic languages, just wait
         | till you try to get into a large codebase without types. Where
         | everything basically devolves into containers of arbitrary
         | things that you don't know what they contain until you've gone
         | through the thing with a debugger.
         | 
         | Maybe your day job becomes more interesting that way. Making
         | CRUD apps is admittedly boring, but this isn't in my opinion
         | the way to keep one's self on their feet.
        
           | joppy wrote:
           | 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 much more easily, since if I
           | want to add the possibility of another type in a functions
           | inputs, I don't have to go and update every callsite.
        
             | dddbbb wrote:
             | 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.
        
             | mplanchard wrote:
             | I agree with my sibling comment regarding Haskell, but your
             | example essentially as written is possible in typescript.
             | Python's type system also allows it via the Union type,
             | e.g. Union[str, int]
        
         | ohgodplsno wrote:
         | 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 pass the
         | rights types when calling it. Doing a very bad job at just
         | being a bad compiler.
         | 
         | Now, I'll agree, the languages you mentioned are absolutely
         | horrible with types. Types in C are basically nothing more than
         | suggestions because you end up casting const away while
         | laughing, C++ has std::types<template typed<std::frobnicate<T,
         | A, R>>>, C# and Java in the past were excessively verbose and
         | required you to type every single thing. Java/C#/C++ finally
         | got a bit better with that with auto/var.
         | 
         | Kotlin types and type inference are absolutely fantastic and
         | make your code clearer.
         | 
         | 2/ No, you don't write fooString, fooInteger, etc. You write
         | foo(value: Int), foo(value: String) and let the type system
         | resolve and tell you which ones are available, rather than
         | relying on documentation and runtime type checkings that are
         | plain bad. And if it makes sense, you can even declare them as
         | extension functions, Int.foo()
         | 
         | 3/ Yes you do. Unless you explicitly opt in to null values or
         | you're using platform types, like calling code from Java with
         | @Nullable/@NotNull annotation
         | 
         | 4/ Nobody calls for generalizations all the time. You're not
         | supposed to write generics for all your methods.
         | 
         | 5/ Explicit definitions are uglier compared to duck typing and
         | just going "yolo it has a .frobnicate() method that means I can
         | call it" ? I agree that over time, they might become unyieldy,
         | but once again, with a proper typechecker and type inference,
         | they're a blessing. Using kotlin and writing
         | when(this) {              is Frobnicator -> this::frobnicate
         | is Zobtrinatex -> this::zobritnex             else ->
         | this@caller::defaultBehavior         }.invoke()
         | 
         | gives you safety, checks that you're not mixing return types.
         | 
         | 6, 7/ Depends. I've written some true abominations, yes.
         | Typecript gets ugly when the libraries you're using abuse types
         | horribly (React used to be absolute trash for that, declaring
         | props was terrible). But then, you pay the cost once (at
         | declaration), and get benefits through your entire app.
         | 
         | Don't write generics for a component, unless it makes sense. If
         | you're exposing a Container<T> and you can access the data
         | inside and you require its type, okay, it makes sense. But
         | don't do a Page<T>, that's dumb.
         | 
         | Generics are a tool. Like C tells you to not abuse (void*),
         | don't abuse generics. Use them with parcimony and your code
         | will be better, give you more guarantess and literally write
         | itself if you've got a competent IDE.
        
           | GlennS wrote:
           | In Python you'd usually use a `Decimal` for money and it
           | would give you an error if you tried to mix it with floats,
           | not silently throw away information (which I certainly agree
           | would be a dire consequence).
           | 
           | In JavaScript, good luck for now, but a solution is being
           | rolled out: [https://caniuse.com/bigint].
        
           | diminish wrote:
           | Thanks @ohgodplsno for the detailed and thoughtful response.
           | 
           | One final point is that with statically typed languages you
           | sometimes fight with the language to deliver. The language
           | and libraries becomes a maze of problems. Developer
           | productivity and sense of accomplishment is low.
           | 
           | I sometimes look at my code when I wrote a matlab clone in
           | C++, It seems like half of the code is things which I wrote
           | to overcome or augment the language and the libraries.
        
           | AlexSW wrote:
           | I'm not following your answer to 2, do you have an example?
        
             | ohgodplsno wrote:
             | Say you'd like to have a foo method that returns... a
             | number, whatever.
             | 
             | You do not need to have multiple symbols "fooString",
             | "fooDecimal", "fooString", etc. Simply overloading the
             | parameters gives you type safety, and keeps a single foo
             | symbol.                   fun foo(value: String): Int =
             | value.length()         fun foo(value: Int) = value
             | fun foo(value: RemoteDatabase) = value.servers.map {
             | it.connect().executeSql("SELECT 1")[0].toInt() }.sum()
             | 
             | All these define a foo method, that returns an Int. The
             | untyped alternative (what javascript, python, etc do in a
             | naive way, without trying to duck type) is to do this:
             | fun foo(value: Any) = when (value) {             is Int ->
             | value             is String -> value.length()
             | is RemoteDatabase -> value.servers.map { ... }.sum()
             | else -> error("Welp, our type system couldn't help us
             | there.")         }
             | 
             | Additionally, if it really makes sense, you can define foo
             | as an extension function on the type:                   fun
             | Int.foo() = this         fun String.foo() = length()
             | fun RemoteDatabase.foo() = servers.map { ... }.sum()
             | 
             | You can then use it directly on the type, rather than call
             | foo(1):                  val fooResult = 1.foo()        val
             | fooStringResult = "Well hello there".foo()
        
         | mplanchard wrote:
         | > 2. When you want to implement a method which applies to
         | multiple libraries, you end up writing `fooString`,
         | `fooInteger`, `fooDatetime` etc (or foo(String), foo(Integer))
         | etc. DRYing is too hard. So you end up writing 10x more methods
         | 
         | This is only a problem in typed languages without generics
         | (like go) or without sum types (like go). With generics, the
         | compiler takes care of writing fooInteger, fooDatetime, etc.
         | With sum types, the compiler verifies that you've handled all
         | of the cases in your foo function.
        
       | mlthoughts2018 wrote:
       | 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.
        
         | dpc_pw wrote:
         | Let me guess. You probably worked in small teams, mostly with
         | code you co-wrote your whole carrier.
         | 
         | You literary compare 3 languages that you happened to use and
         | think that all there is to know about this argument.
        
       | adnzzzzZ wrote:
       | 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 people who swear by typed
       | languages that they simply like creating extra work for
       | themselves as a way to avoid doing what's actually needed to be
       | done, just because they want to feel productive.
       | 
       | I also suspect that a lot of this has to do with people's
       | personalities around the concept of borders. Some people like
       | well defined borders in general in everything they do because
       | they approach life from a more procedural perspective, and for
       | procedures to work they need things to be in the right boxes and
       | in the right places. While others prefer borders to be undefined
       | and more free-flowing because more information can pass through
       | concepts and that allows for a more unstructured design process.
       | It only puzzles me that there's so much energy in indie game
       | development for highly bordered programming environments (i.e.
       | all the energy being put into gamedev Rust libraries) when indie
       | developers tend to be people who value borders less, as do all
       | creative types. But I guess people really like types...
        
         | [deleted]
        
         | jondubois wrote:
         | I agree. I think the downside of static typing is that it
         | encourages developers to pass around complex types between
         | functions instead of simple types and I think this is a
         | mistake.
         | 
         | If you have the option between creating a function which
         | accepts a string (e.g. ID) as argument or accepts an instance
         | of type SomeType, it's better to pass a string because simple
         | types such as strings are pass-by-value so it protects your
         | code from unpredictable mutations (which is probably the single
         | biggest, hardest to identify and hardest to fix problem in
         | software development). I think OOP gets a lot of blame for this
         | and it's why a lot of people have been promoting functional
         | programming but this blame is misguided; the problem is complex
         | function interfaces which encourage pass-by-reference and then
         | hide mutations which occur inside the blackbox, not mutations
         | themselves. Mutations within a whitebox (e.g. a for-loop) are
         | perfectly fine since they're easy to spot and happen in a
         | single central place.
         | 
         | If you adopt a philosophy of passing the simplest types
         | possible, then you will not run into these kinds of mutation
         | problems which are the biggest source of pain for software
         | developers. Also you will not run into argument type mismatch
         | issues because you will be dealing with a very small range of
         | possible types.
         | 
         | Note that this problem of trying to pass simple types requires
         | an architectural solution and well thought-out state management
         | within components; it cannot be solved through more advanced
         | tooling. More advanced tooling (and types) just let you get
         | away with making spaghetti code more manageable; but if what
         | you have is spaghetti code then problems will rear their ugly
         | heads again sooner or later.
         | 
         | For example, a lot of developers in the React community already
         | kind of figured this out when they started cloning objects
         | passed to and returned from any function call; returning copies
         | of some plain objects instead of instances by-reference
         | provided protection from such unexpected mutations. I'm sure
         | that's why a lot of people in the React community are still
         | kind of resistant to TypeScript; they've already figured out
         | what the real culpit is. Some of them may have switched to TS
         | out of peer pressure, but I'm sure many have had doubts and
         | their intuition was right.
        
           | nicoburns wrote:
           | 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.
        
             | jondubois wrote:
             | 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/components, you're
             | leaking abstractions between different components.
             | Sometimes it is appropriate to do this, but most of the
             | time it's dangerous. Components should aim to communicate
             | as little information about their internal state to other
             | components as possible.
        
               | viraptor wrote:
               | 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.
        
           | willtim wrote:
           | If you use String for all your data types than you are no
           | better than a dynamic language. There are many string like
           | things that benefit from their own types, e.g. currency,
           | identifiers, post codes. Such types should only be created
           | from a parse of valid strings, i.e. no empty strings,
           | whitespace, illegal values etc. They do not have to be Alan
           | Kay "objects", despite what your language or thought
           | leadership is telling you. They should be values with value-
           | based equality. A modern statically typed language should let
           | you define such a type in a few lines. This is all done in
           | order to make illegal states unrepresentable, which is what
           | type systems are for.
        
           | alkonaut wrote:
           | The correct thing is imho to pass an immutable SomeType or an
           | interface that only exposes the parts of SomeType necessary
           | for the calculation and doesn't allow mutation of the object.
           | 
           | Of course you don't send around references to mutable objects
           | and of course you only send to a function just what it needs
           | - but that's regardless of type system.
        
             | jondubois wrote:
             | Sometimes this will be the best approach possible but
             | adhering with this principle too strongly can
             | overcomplicate the general design/architecture - It can
             | give developers a green light to start passing around
             | complex types all over the place and harms the separation
             | of concerns principle.
             | 
             | In terms of modularity and testability, the ideal
             | architecture is when components communicate with each other
             | in the simplest language (interface) possible. Otherwise
             | you become too reliant on mocks during testing (which add
             | brittleness and require more frequent test updates). I
             | think very often, static typing can cause developers to
             | become distracted from what is truly important;
             | architecture and design philosophy. I think this is the
             | core idea that Alan Kay (one of the inventors of OOP) has
             | been trying to get across.
             | 
             | 'I'm sorry that I long ago coined the term "objects" for
             | this topic because it gets many people to focus on the
             | lesser idea. The big idea is "messaging"' - Alan Kay
             | 
             | It's very clear from Alan Kay's writings that when he was
             | talking about 'messaging' he was talking about
             | communication between components and he did not intend for
             | objects to be used in the place of messages.
        
               | [deleted]
        
               | loup-vaillant wrote:
               | Abandoning a feature just because it enables a misuse is
               | the wrong way to do it in my opinion. Yes, some
               | inexperienced, stubborn, stupid, or hurried developers
               | will pass around complex types when they really
               | shouldn't. But no, this drawback does not nullify the
               | _massive_ advantages of (good) static typing.
               | 
               | Sure, interfaces should be kept small. Let's to just
               | that, then! Recognise that we want our
               | classes/functions/modules to be deep (small
               | interface/implementation ratio), and frown upon shallow
               | instances in code reviews.
               | 
               | No need to give up static typing.
        
           | marcosdumay wrote:
           | You seem to have experience only on languages where strings
           | are immutable, and objects are always mutable. Those
           | properties are not universal.
           | 
           | And yet, despite correctly assessing the problem, you insist
           | on fighting objects instead of mutability.
        
           | valuearb wrote:
           | In Swift Structs are immutable and passed by value, class
           | objects are mutable and passed by reference.
           | 
           | Using a struct is infinitely preferable to a string.
        
         | sideshowb wrote:
         | Maybe it's a case of problem complexity. I think of types as a
         | tool to help cope with certain things. If you're building a
         | garden wall you probably don't need CAD. For a 747, you
         | probably do. Though aircraft predate CAD so it's clearly not
         | impossible to do without.
         | 
         | I dislike types in 30 lines of python because they're
         | unnecessary complexity. I like them in 10000 lines of c++
         | because they do some of the thinking on my behalf.
        
           | i6ruce wrote:
           | > I dislike types in 30 lines of python They are already
           | there you just don't want to acknowledge them. You can build
           | the same prototype in strictly typed language just by
           | sticking to some primitive types like int/string and type
           | inference, and the progress toward something more complex as
           | your prototype grows. I personally prefer to use types right
           | away, so type system can guide me further and show me when
           | I'm assuming something in a wrong way.
        
         | awild wrote:
         | What languages have you worked with? As per the OP, there are
         | certain languages where types are much more expressive and add
         | something to the programming experience.
         | 
         | Learning haskell changed a lot about the way I think about
         | programs and that is even as someone who primarily writes in
         | java.
        
         | samhain wrote:
         | 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 is run during development, where
         | resources are unconstrained, and stability is not an issue,
         | should absolutely value the time it takes to develop and
         | maintain the script. So using a typed language may not be
         | useful here. A situation where small optimizations make large
         | improvements might benefit from a typed language, maybe a
         | physics engine of a game intended for multiple platforms?
         | 
         | In the OP, the author approaches it from a "systems"
         | perspective, that when you need either of the 3 scenarios, then
         | you might consider using types. Type inference, Sum/tagged
         | union types, and Soundness, which I think could easily apply to
         | certain areas of game development. Ignoring the nuance around
         | the issue, and being dogmatic that all scenarios in a given
         | field do not need types is ignoring that what we're really
         | doing is writing in languages that need to be interpreted by
         | both humans and machines.
        
           | darkerside wrote:
           | 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.
        
         | loup-vaillant wrote:
         | Statically typed languages (or at least the good ones) are
         | disciplined so the programmer doesn't have to.
         | 
         | If you can work reliably with dynamic typing, that means you
         | are very disciplined about giving the right data to the right
         | function, in exactly the right form. That you are very
         | disciplined about tests, possibly including fairly stupid-
         | looking unit tests (which aren't _actually_ stupid, at least in
         | a dynamic context). Adding static typing on top of that wouldn
         | 't help much of course.
         | 
         | When I write something from scratch however, I found that
         | static typing actually _speeds up_ my development. It 's _less_
         | work, not more. Because I don 't have to write as many tests,
         | or even _worry_ about huge classes of errors -- the compiler
         | (and if I 'm lucky, my editor/IDE) just checks them for me.
         | 
         | I don't know the work you do, but I bet that your style could
         | benefit from some static checks. Perhaps not the mainstream
         | ones, but your scripts _work_ somehow, don 't they? That mean
         | they respect a number of invariants, some of which could
         | certainly be checked at compile time, saving you significant
         | time on stupid bugs. The result won't be TypeScript or Rust,
         | but I don't think it would be fully dynamically typed either.
        
           | TeMPOraL wrote:
           | I can second the experience. I write a lot of Common Lisp,
           | and these days it's typed Common Lisp for me. It adds very
           | little overhead in terms of code writing speed, but
           | continuously stops me from making stupid mistakes (like e.g.
           | forgetting a function I'm calling returns a sequence and
           | treating it as a scalar value). My comfort of writing is much
           | better, because I spend less time in interactive debugger
           | hunting my own typos.
        
           | williamdclt wrote:
           | > I found that static typing actually speeds up my
           | development. It's less work, not more.
           | 
           | It's a point that comes back often, and that I totally agree
           | with so it's worth reiterating. In addition to the improved
           | dev tooling (autocompletion, hinting, refactoring), being
           | able to write large swathes of code without actually running
           | it and being 100% confident that it's all _valid_ (not bug-
           | free of course) just takes a huge load off my mind.
           | 
           | Of course, there's huge differences between languages like
           | Java and languages like Typescript. Talking about "typed
           | languages" as a homogenous concept often doesn't make a lot
           | of sense
        
             | pansa2 wrote:
             | > being able to write large swathes of code without
             | actually running it and being 100% confident that it's all
             | _valid_ (not bug-free of course) just takes a huge load off
             | my mind.
             | 
             | I've heard similar things before, e.g. "static typing
             | allows you to find bugs in your code without even running
             | it".
             | 
             | Perhaps the reason I'm a fan of dynamically-typed languages
             | is that I don't see the benefit of this. Maybe my workflow
             | is unusual, but I don't write code without running it - I
             | run tests every time I add a few lines.
        
               | loup-vaillant wrote:
               | OCaml has a REPL. I use it all the time to check that a
               | new function I just wrote is correct. Yet I still get
               | huge benefits from the static typing: many of my errors
               | are stupid type errors, and having the type checker tell
               | me about them, rather than a runtime error (or worse, a
               | wrong result), makes early prototyping much faster.
               | 
               | Even if I already have a REPL. I believe the main reason
               | is because the type checker is much closer to the source
               | of my errors than runtime checks or tests are.
        
               | adnzzzzZ wrote:
               | I think the same. This is especially true for games where
               | you're absolutely running the game again for everything
               | you change, and in case anything is wrong it's generally
               | very obvious visually.
        
           | asiachick wrote:
           | how many times have you not used bash/dos/pwsh scripts
           | because it's not typed?
           | 
           | Maybe there's a place for both?
        
             | loup-vaillant wrote:
             | I can say what I _do_ use Bash for: create files  &
             | directories, and simple string replacements in files.
             | Anything more involved goes into a proper program. Usually
             | OCaml, though I can fall back to more mainstream languages
             | (Python, C) if I need a wide audience to be able to read
             | it, _and_ the program is simple enough that types aren 't
             | really a problem to begin with.
        
             | Aeolun wrote:
             | Javascript is for scripts that fit in one file. Anything
             | else is typescripy.
        
           | orwin wrote:
           | Yes. Try prototyping for a quick POC/casual demo with
           | javascript, then try with typescript. If you get back to your
           | demo two month later (or have one other person to explain
           | your code to), typescript is Infinitely superior.
           | 
           | For quick hacks types are not useful though.
        
             | i6ruce wrote:
             | Unless you're quite experienced with them and use as a
             | thinking tool and not as burden.
        
         | sammorrowdrums wrote:
         | I think it depends on size or codebase, how many people are
         | working on things and how long you can afford to develop before
         | releasing.
         | 
         | I do mostly work with python and JS, but last Christmas I
         | learned Rust, and it strongly occurred to me that exhaustive
         | matching, no nulls, borrow checking and strong type inference
         | would be a real boost to development given the initial time to
         | build the codebase up. I'd put money on them removing hours of
         | hunting subtle bugs, and on missing the ramifications of
         | refactors.
         | 
         | I built some small scale game stuff using SDL2 for Advent of
         | Code and I enjoyed rust for doing that a lot.
         | 
         | I think also dynamic languages work best when developers
         | actually are knowledgeable about the underlying types and
         | effectively write code in a typed manner anyway. It's a much
         | worse trade-off when function signatures actually avail of
         | loose typing to do strange things.
        
       | cel1ne wrote:
       | Types are important and necessary.
       | 
       | Can you skip them in a typed language? Yes, just use any, Object
       | or whatever the equivalent.
       | 
       | Can you add them to an untyped language? No.
       | 
       | They are not needed anywhere. But I argue that especially
       | JavaScript module-systems would have benefited greatly from them.
       | 
       | A million lost hours in fixing obscure "undefined is not a
       | function"-errors from output of highly dynamic pluggable
       | build/transpiler-systems like webpack, requirejs, babel, buck
       | etc. could have been avoided.
        
         | nlitened wrote:
         | JavaScript is not untyped, it's dynamically typed. The types
         | are still there, they are just not enforced at compile time.
        
           | Ericson2314 wrote:
           | No "dynamically typed" is an oxymoron, and untyped or
           | "unityped" is correct.
           | 
           | Please read
           | https://existentialtype.wordpress.com/2011/03/19/dynamic-
           | lan..., the classic take-down of this mistake.
        
       | fmakunbound wrote:
       | Static types are great for Fungeable Programmer at Big Co. where
       | you can be dropped into a project and it sort of gives you a way
       | to figure out what's present in the code.
       | 
       | However, if you need to get somewhere fast, be it a side project
       | where you've only got so much time due to other constraints in
       | life, or it's a product you're trying to get to market first with
       | a small team, then you're not going to benefit from ossifying
       | statically typed languages - you're going to reach for something
       | that's interactive, powerful, and not bogged down in
       | edit/compile/test cycles. You're going to instead reach for
       | something like Common Lisp, Ruby, Python etc.
       | 
       | Once you're making $COMFY_DOLLARS_PER_MONTH you can then pass it
       | off to some re-write team, though.
        
       | hohohmm wrote:
       | The benefits of types is not something to be discovered, but
       | something that's taught in school with very convincing arguments,
       | if not too much zeal. It's interesting to see this kind of post.
       | Not to be sarcastic about the late discovery of typed goodness,
       | but the fact that this is not already a concensus in the
       | engineering world.
        
         | christophilus wrote:
         | It's not a consensus because many of us have found that types
         | are not the panacea we were taught. Erlang is a fantastic
         | language, and it's arguable that types would not improve it,
         | but rather hinder some of its better features. Same is true for
         | Smalltalk and various lisps. I think there's a place for
         | various approaches to types.
        
           | orwin wrote:
           | All lisps I used could be statically typed, I think? Does
           | elisp used typep?
           | 
           | For small projects, hacks or emacs macros types are not
           | useful but the possibility of adding types is there.
           | 
           | [Edit:and it's useful when your finite state machine grows to
           | much]
        
       | aazaa wrote:
       | > 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.
        
         | jenscow wrote:
         | Personally, I prefer the type names to be at the start of the
         | line, so I don't need to scan to the end of the line (or guess
         | based on a function name).
         | 
         | But I agree, it's a waste to type it all out. So I use an
         | intelligent IDE that reduces the repetitive typing:
         | 
         | So typing `Person.var` gives me `Person person = new Person();`
         | or typing `Person person = ` will suggest `new Person()`
         | 
         | Sure, it doesn't look as appealing when creating an new object,
         | however I get a better information when you've written
         | something like:                   Person me =
         | something.GetOwner();
         | 
         | rather than:                   let me = something.GetOwner();
        
           | mannykannot wrote:
           | I think you are making a good case for a somewhat different
           | point: we have long reached the point where the tools we use
           | for writing and reading programs can give us all sorts of
           | semantic information on demand, and language syntax design
           | should take advantage of the fact that everything the
           | programmer needs to know need not be forced into just one
           | flat page-of-text view of the code.
           | 
           | Alan Kay made essentially this suggestion when the
           | requirements for the language that became Ada were being
           | debated, but it was not taken up at that time.
        
             | jenscow wrote:
             | But that doesn't mean hide everything from view just
             | because it can be accessed via a keyboard short-cut or
             | mouse movement - my eyes are faster and easier to move.
             | Also, code isn't read exclusively in an IDE, nor the same
             | IDE that I use.
        
           | II2II wrote:
           | In the case of Java you have the option of using "Person me"
           | or "let me". Simply stick to the convention of explicitly
           | stating the type at least once when declaring a variable. I
           | have also noticed that Python allows developers to note the
           | type (at least when defining functions). It's not necessary
           | and I don't think the language uses it, but at least it
           | offers a standard mechanism to note it in the code.
        
           | lights0123 wrote:
           | All the major Rust IDEs (VS Code, IntelliJ, and probably Vim
           | + others) show the variable type inline when you don't
           | provide it explicitly: https://i.imgur.com/96Wfukl.png
           | 
           | The white text on a gray background is provided by my IDE.
        
         | svieira wrote:
         | Once you get to Java 11+ you get `var`, so you can spell it
         | `var me = new Person()`.
        
           | mgkimsal wrote:
           | Was recently on a project that just migrated to java 11 (mid
           | august of this year) from java 8.
           | 
           | Someone has started to try to introduce 'var' and is getting
           | push back from others. "it doesn't match current style" and
           | "could be confusing" were some 'concerns'.
           | 
           | When you're trying to do
           | InternalFormatParserCustomForClientABCDEF
           | customerFormatParser = new
           | InternalFormatParserCustomForClientABCDEF(param1, param2);
           | 
           | you really start to hit readability limits, and formatting
           | things like going over nominal line lengths and such.
           | var customerFormatParser = new
           | InternalFormatParserCustomForClientABCDEF(param1, param2);
           | 
           | is certainly easier on the eyes, and the compiler can still
           | known and infer what type 'customerFormatParser' is.
        
         | aembleton wrote:
         | You might enjoy Kotlin. Type inference, with full compatibility
         | with Java libraries and ecosystem.
         | 
         | Similar to Rust, you can just write `val me = Person()`. No
         | need for the new keyword.
        
           | eru wrote:
           | Though keep in mind that not all type inference is created
           | equal.
           | 
           | Ie what you find in Haskell or OCaml is much more powerful
           | than what eg Go gives you. (In Go type inference only works
           | 'forward'.)
           | 
           | (I don't know enough about Kotlin to know what kind of type
           | inference it has.)
        
           | edem wrote:
           | I love Kotlin. I also love Typescript. I'm yet to try Rust,
           | but I'm positive that I'll love it as well. They are all born
           | to solve the same problem I think, only from a different
           | perspective. I use Kotlin every day and the more I learn the
           | happier I am. I'd never go back to Java if I can help it.
        
       | shortercode wrote:
       | Interestingly quite similar to my own progression. I think
       | working in modern typed languages ( Rust, Swift, Typescript ) is
       | what primarily changed my viewpoint in a way that C and Java just
       | couldn't.
       | 
       | In the last year I've transitioned from full time JS development
       | to full time TS development and have been pleasantly surprised
       | how easy the transition was for my personal projects. Previously
       | I had been quite adamantly against TypeScript; seeing the type
       | system as an unnecessary level of complexity given I already
       | structured my code in quite strict ways. In most it helped me
       | find 1 or 2 small errors, but it certainly helps reduce the
       | amount of time I spend verifying the behaviour of code. It
       | definitely has its flaws, but most of them are linked to its
       | compatibility with JS and I don't think they can be resolved
       | unfortunately.
        
       | nlitened wrote:
       | It looks to me as if people who started with dynamic types
       | discover static types after many years, and vice versa.
       | 
       | There's a zen koan about it, I am sure.
        
       | dustingetz wrote:
       | Hate on types seems to stem from three perspectives I can see:
       | 
       | 1. 2000 era Java-likes cause superlinear class complexity
       | explosion
       | 
       | 2. 2010-era typed functional programming - Scala stdlib type sigs
       | that didn't fit in a tweet, 7 scala stdlib rewrites and churn
       | related to really figuring out how to even do functional
       | programming in applied domains like crud apps
       | 
       | 3. All along, cutting edge comp sci research being presented as
       | the "correct" way to program and you're a normie drooler if you
       | haven't read yesterday's paper and rewritten your stuff onto it
        
       | echelon wrote:
       | 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 strong static types. Sum types let you combine multiple
       | return types elegantly and not be sloppy. Option types remind you
       | to check for an absent value.
       | 
       | Static types let you refactor and jump to definition quickly and
       | with confidence.
       | 
       | Your interfaces become concrete and don't erode with the sifting
       | sands of change. As an added bonus, you don't need to
       | precondition check your functions for type.
       | 
       | Types are organizational. Records, transactional details,
       | context. You can bundle things sensibly rather than put them in a
       | mysterious grab bag untyped dictionary or map.
       | 
       | Types help literate programming. You'll find yourself writing
       | fewer comments as the types naturally help document the code.
       | They're way more concrete than comments, too.
       | 
       | With types, bad code often won't compile. Catching bugs early
       | saves so much time.
       | 
       | Types are powerful. It's worth the 3% of extra cognitive load and
       | pays dividends in the long haul. Before long you'll be writing
       | types with minimal effort.
        
         | NicoJuicy wrote:
         | The incoherent thinking I see with some people even in typed
         | systems, would make me very scared to let them do the same in
         | dynamic ones.
        
           | ooobit2 wrote:
           | Nothing good comes easy. The dozens of hours I'd spend
           | staring at 26 lines in R just trying different ideas to
           | shorten/optimize/improve clarity, and that wasn't something I
           | needed to sell that someone else would depend on for business
           | or personal use.
           | 
           | But I can relate to the pressure to deliver quick results. I
           | found myself burnt out when working on a forecast model
           | around three years ago. The constant "how's it goin'?" tore
           | my attention away from the work, and I'm still convinced I
           | could have delivered a better result.
           | 
           | So, in a way, I agree. In another, I understand the other
           | side of the issue, and I think there are so many less time-
           | intensive tasks going on around engineering that there's
           | often little awareness that something like refactoring a
           | class for better efficiency pays in smaller but compounding
           | ways long-term, with most of the time cost and perceived
           | opportunity cost being immediate and short-term. It's still
           | worth it if you really do the math on the long-term benefit.
        
         | eru wrote:
         | Sum types even work when you actually have the multiple of the
         | same return types. (Ie in Haskell `Either String String` works
         | just as well as `Either String Int`; the types don't have to be
         | distinctive.)
        
           | karmakaze wrote:
           | That follows from the definition being tagged 'Left'/'Right':
           | Either a b = Left a | Right b
        
         | masklinn wrote:
         | > 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.
         | 
         | To me that was not the issue. It was, rather, discovering
         | languages with powerful and expressive type systems.
         | 
         | My first job was in Java, most of my career afterwards was in
         | Python. I've been type-curious for a while because of Haskell
         | and OCaml and am _very_ fond of Rust, I 'd take a job in any of
         | those happily.
         | 
         | Types in Java are still, today, largely verbose, hideous and
         | extraneous. The cost / benefit is extremely low (or rather
         | extremely high, you pay a very high cost for limited benefit,
         | and the cost generally increases faster than the benefits). You
         | _can_ leverage types heavily, but it creates a codebase which
         | is ridiculously verbose, inefficient (because every type is
         | boxed), opaque, and simply doesn 't look like any other Java
         | codebase so will be very much disliked by most of the people
         | you're working with. And the benefits from that will still, at
         | the end of the day, be rather limited.
        
         | TeMPOraL wrote:
         | 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 of it to both optimize code and provide some static
         | typechecking at compile time (with type inference). So my Lisp
         | code now is exploiting this, and is littered with type
         | declarations.
         | 
         | However, the CL type system is very much lacking compared to
         | Rust or Haskell. I'm hoping one day someone will make a
         | statically, strongly typed Lisp that still doesn't sacrifice
         | its flexibility and expressive power. I'd jump to that in an
         | instant.
        
           | stevelosh wrote:
           | > However, the CL type system is very much lacking compared
           | to Rust or Haskell. I'm hoping one day someone will make a
           | statically, strongly typed Lisp that still doesn't sacrifice
           | its flexibility and expressive power. I'd jump to that in an
           | instant.
           | 
           | https://github.com/stylewarning/coalton looks promising, and
           | stylewarning has recently said he's still working on it.
        
           | cconroy wrote:
           | "statically, strongly typed Lisp that still doesn't sacrifice
           | its flexibility and expressive power"
           | 
           | SML
        
             | TeMPOraL wrote:
             | ... with sane (i.e. s-expression based) syntax.
             | 
             | :).
             | 
             | But I'll check out SML. That's Standard ML, right?
        
               | cconroy wrote:
               | Yeah.
               | 
               | ML syntax is very pleasant, and roughly, sexprs w/o all
               | the punctuation noise.
               | 
               | I don't believe it has a macro capability like lisps
               | though, but you gain a sophisticated type helper.
               | 
               | Definitely worth looking into!
        
               | Ar-Curunir wrote:
               | FWIW, SML is an old research-focused language that was
               | the progenitor of Haskell and Ocaml and Rust, and not
               | something to program in :)
        
           | gryfft wrote:
           | My experience with strong types is limited-- I'd done the
           | thing of learning some C, doing professional stuff in Ruby
           | for several years and then discovering the ridiculous power
           | strong types can have and doing some professional stuff in
           | Go.
           | 
           | Typed Racket [1] was really a revelation to me in that
           | regard. I'd be curious how developers with more strongly-
           | typed language experience feel about it.
           | 
           | [1] https://docs.racket-lang.org/ts-reference/index.html
        
       | christophilus wrote:
       | I was a statically typed fanboy for most of my early career. C#
       | and later F# were my daily drivers and still hold a fond place in
       | my heart. More recently, I learned TypeScript and a bit of
       | Haskell and Rust.
       | 
       | However, I think dynamic languages have their place. Most of my
       | server side code involves parsing one string and transforming it
       | into another (JSON to SQL or the like). Something like Clojure
       | spec is really, really useful for this, and beyond that the
       | remaining code doesn't really materially benefit from static
       | types.
       | 
       | At any rate for my typical web application, I now prefer dynamic
       | languages, which is something I never thought I'd say.
       | 
       | One last thought: soundness is just one variable to optimize for,
       | and it's not as important as I once thought. For most parts of my
       | application, rough edges are not a big deal. For the really
       | important stuff (like payments), I always write tests and also do
       | a fair amount of manual testing. And for that stuff, the bugs are
       | almost always logic bugs that types wouldn't have caught.
        
         | Ericson2314 wrote:
         | > Most of my server side code involves parsing one string and
         | transforming it into another
         | 
         | But this is the real problem. It's a huge failure that so much
         | programmer effort goes into writing the same broken marshaling
         | code over and over again. It's unproductive, boring, and if you
         | believe in http://langsec.org/ also the major source of
         | security issues.
        
         | edwinyzh wrote:
         | How about a language that supports both strong typed data type
         | and dynamic data types? Not sure about other language, but Free
         | Pascal and Delphi support both of them.
        
       | TooCreative wrote:
       | I code without types. My "proof", that types do not pay for
       | themselves goes like this:
       | 
       | Every time I encounter a bug (during coding, testing or in
       | production) I make a note what type of bug it was and how it
       | could have been prevented.
       | 
       | Types are way down on the list of what could have prevented the
       | bug. Especially for production bugs, which are the most important
       | of course. It is so rare, that a bug could have prevented by
       | types that I can say with confidence that they would have been a
       | net negative.
       | 
       | Talking about which tool can prevent the most bugs, integration
       | tests win by a large margin.
       | 
       | The reason is that most bugs are conceptual. Like "Oh shit! We
       | have allowed people to tag items as duplicates of other items.
       | And we have a function that traverses the list up to the original
       | item. But now this new feature over there had a bug where it
       | marks the last original as a duplicate of another duplicate and
       | then when the traversal function in that other module is used, it
       | ends up in an infinite loop".
       | 
       | Another example of a popular bug category: The code contains
       | assumptions about the environment that do not hold true. For
       | example PHP's mb_strtolower() will not always create the same
       | string as MySQL's LOWER(). It is very rare and only holds true
       | for a tiny tiny fraction of the UTF-8 characters. So you might
       | expect them to behave the same until you one day trip over one of
       | those few chars.
        
         | [deleted]
        
         | simion314 wrote:
         | Maybe your style is not fit for types?
         | 
         | So I fixed a bug recently where a url validatior code was
         | falling because the code was old and recently what is allowed
         | in an url changed. Imagine there was a language where
         | url/email/file path was a type that could validate itself when
         | you create it and you don't need to always try to create regex
         | to validate things. Even more cool would be if string would not
         | be something we use daily (like we don't use every day bytes)
         | so we would have always a type for customer name, file name or
         | file path and you would need to explicitly ask a conversion
         | from a customer name to a file name etc. The way I think future
         | languages and types could help is to make it almost impossible
         | co create invalid data structures or state.
        
         | tluyben2 wrote:
         | I think it takes people who do not start out 'believing in
         | types' (I was taught by pupils of dijkstra in NL so my belief
         | in strict-as-possible has always been quite firm) a same kind
         | of timespan / experience as the OP; experience (very) large
         | weak/stringy typed projects, experience them for at least a few
         | years full time and then, when the frustration sets in, try
         | something which is the complete opposite like Haskell/Rust.
         | 
         | I think a certain frustration needs to be there to try
         | something else anyway, when you come against the billionth
         | cannot call hello() on undefined error in a critical (for the
         | company), 100k+ LoC, multi-team project, you might wonder if
         | there is something else.
         | 
         | You are probably not going to post your 'list' here but proper
         | types prevent many trivial and non trivial bugs. Many of the
         | points on your list will fit there, but you wouldn't know it
         | yet.
         | 
         | > Talking about which tool can prevent the most bugs,
         | integration tests win by a large margin.
         | 
         | Obviously there are ways of catching them without types, but
         | proper type systems catch them at compile time and also; how
         | are these mutually exclusive; we use both. We just need less
         | integration tests.
         | 
         | > Like "Ooooooh shit! We have allowed people to tag items as
         | duplicates of other items. And we have a function that
         | traverses the list up to the original item. But now this new
         | feature over there had a bug where it marks the last original
         | as a duplicate of another duplicate and then when the traversal
         | function in that other module is used, it ends up in an
         | infinite loop".
         | 
         | In some of my favorite languages you can catch this in types at
         | compile time.
         | 
         | Obviously; do what works best for you and your team; I just
         | don't buy your overarching statements and 'proofs'. If it works
         | it works, but it would _probably_ work better with types.
        
           | kybernetikos wrote:
           | Working on a large scala codebase, I quickly learnt that
           | 'compile time' in one language does not always happen before
           | 'run time' in another language.
           | 
           | What matters is not the phase in which the bug is found but
           | how close in absolute time finding the big is to writing it.
        
             | tluyben2 wrote:
             | 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', but the fall-out, and again ofcourse
             | YMMV, of having statically preventable bugs popping up in
             | Sentry at 3 am in the morning with things you would've
             | prevented (not necessarily directly by the types but you
             | would've thought about it more because you had to define
             | the types, which is I think what the parent poster here
             | misses too; I just tend to think less and try more without
             | types which, again for me, is a bad state, but ymmv) is not
             | great.
             | 
             | But sure, I am biased as my experience with statically
             | typed langs has been good since I moved from asm/basic in
             | the 80s to pascal/c (they were an improvement over
             | asm/basic and my first experience with types, not saying
             | you should use them now, or not).
        
               | kybernetikos wrote:
               | 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 compile time and slow down the iteration time (one of
               | the more important things for programming in my view). In
               | my view, saving the source and seeing the result almost
               | immediately in a browser is one of the big advantages of
               | web development.
               | 
               | 2. Types are almost always written in a second, much less
               | powerful DSL and then sprinkled distractingly through the
               | code that actually does the work. I prefer the way
               | Haskell does this- separate the type signature out onto
               | at least a separate line rather than mashing the two
               | different languages together.
               | 
               | 3. Higher levels of abstraction tend to become very hairy
               | in many type systems (although not all). This ends up
               | just meaning that people who like types often restrict
               | themselves (unconciously) to less abstract programming.
               | They spot the time they're saving by avoiding some kinds
               | of bugs, but they don't see the time they're wasting by
               | being unable to talk at a higher level of abstraction.
               | Another way this shows itself is that types are very
               | rarely first class objects in strongly typed languages,
               | making it very difficult to create code that operates on
               | types, or understands types.
               | 
               | 4. Type systems open up opportunities for type driven
               | architecture astronauting, which is just yet another way
               | you can go down an unproductive rabbit hole. There was an
               | interesting study done on different teams solving
               | problems with different languages. The differences of
               | different teams within the same language was much bigger
               | than between languages, but the team that made slowest
               | progress (and without particularly having an unusually
               | low number of bugs) was the team that leant the hardest
               | into encoding everything in the type system.
               | 
               | 5. Type systems encourage code generation build
               | pipelines, which again slows iteration time and makes
               | everyones life miserable.
               | 
               | 6. Type systems reflect a incorrect model of the world -
               | user input, network input, file system data _is not
               | typed_. The misery that I 've had with some web server
               | frameworks that refuse to acknowlege that they don't know
               | every possible thing that the web client might send them
               | and are able to slot it into a predefined type. I think
               | this is the same kind of error that we made with OO
               | systems - thinking that we could fit the world into a
               | predefined inheritance hierarchy.
               | 
               | 7. Type systems encourage a static view of the world. The
               | types of things can change under you, dynamically, (e.g.
               | the structure of a table in a database), but in most
               | typed languages you can't cope with that correctly
               | without shutting down and deploying entirely new code.
               | 
               | 8. Related to that, it's hard to imagine using a strongly
               | typed language with the live image approach of smalltalk
               | or sometimes used by lisp systems. This means that the
               | popuarity of strongly typed languages is killing valuable
               | and interesting approaches to building complex systems
               | that emphasise observability, interaction and iteration
               | as a way of understanding them.
               | 
               | There are genuine advantages to typed languages, but many
               | of the advantages touted as being unique to typed
               | languages can be provided by advanced linting and IDEs
               | (intellij was surprisingly capable on plain JS + jsdoc).
               | You can also ameliorate some of the disadvantages of
               | untyped languages while keeping the benefits by
               | deliberately programming in a fail-fast way.
               | 
               | I'm sure that type systems have their place. The research
               | I've come across on empirical studies suggests that while
               | there may be positive effects they are small, which does
               | not at all mesh with the extreme partisanship I generally
               | observe. Yes, type systems gain you something, but there
               | seems much less awareness of what you lose.
        
               | marcosdumay wrote:
               | There's a lot of things, some about old-school types
               | (Java, C), other about modern ones. I don't think most
               | are fundamental, even though some are common experiences
               | today.
               | 
               | #1 is fundamental. (Yet people somehow live with the JS
               | ecosystem that's slower than GHCi.) It's supposed to
               | evolve into always becoming a smaller problem, since
               | computers are always getting faster; but I don't think
               | we've put everything we can into types already, so I
               | expect it to get worse in the near future.
               | 
               | #2 and #3 are about old-school types.
               | 
               | #4 Oh, yeah, they can. But they can also help a lot in
               | team coordination. Powerful stuff enable you either way,
               | if you harm yourself or take advantage of it is your
               | choice.
               | 
               | #5 Failures in type systems encourage code generation.
               | Expect that to always improve, but always slowly.
               | 
               | #6 That's why there's always a parsing stage between
               | input and processing. You deal with input errors at the
               | parsing stage, and processing errors at the processing
               | stage. Most communities of dynamic and old-school
               | languages make a large disservice to the industry by
               | mixing those; they explode error handling into something
               | intractably complex.
               | 
               | #7 Hum... You are holding it wrong. Do not state variants
               | into your types. Instead, use the type system to get
               | every invariant out of the way, so the variants stand
               | clear. (And yeah, there are plenty of libraries and
               | frameworks out there that try to encode the environment
               | into types. That deeply annoys me... But anyway, if you
               | do that, take the types as requirements upon the
               | environment, not its description. Those are different in
               | very subtle ways.)
               | 
               | #8 This shouldn't be fundamental. AFAIK there are not
               | many people trying this, and the few there face a
               | Sisyphean task of keeping their code up to date with the
               | mainstream changes. I do hope people make progress here,
               | but I'm not optimistic.
        
               | kybernetikos wrote:
               | > #2 and #3 are about old-school types.
               | 
               | There absolutely are approaches to this that don't fall
               | foul of my complaints, but when you say 'old-school
               | types' I think you're talking about Java and non inferred
               | types.
               | 
               | I was including other more modern languages in my
               | criticism. Scala for example ends up with pretty hairy
               | types very quickly for higher level code. So much so that
               | they made the documentation system lie about the types to
               | make it easier to understand.
               | 
               | And most currently popular languages don't give you
               | runtime access to types and allow you to treat them as
               | first class.
               | 
               | The languages that allow you to deal with types with the
               | same language you write code in are not remotely
               | mainstream. So unless by 'old school' you include all
               | mainstream languages then I disagree.
        
               | marcosdumay wrote:
               | Yes, I meant types systems like Java's.
               | 
               | I was thinking about unusable code, cause by the need to
               | write way more down in brittle types than anything you
               | save on coding. In fact there are problems with complex
               | types.
        
               | tluyben2 wrote:
               | > Yet people somehow live with the JS ecosystem that's
               | slower than GHCi.
               | 
               | I think a lot of people, including the parent, seem to
               | equate speed of ecosystem and iteration with web
               | development and instant reload of web pages. When other
               | systems allow fast iteration, it goes unnoticed unless
               | it's for web dev. Luckily, a bunch of those 'impossible'
               | systems have to now too, like [0].
               | 
               | [0] https://ihp.digitallyinduced.com/blog/2020-08-10-ihp-
               | live-re...
        
               | kybernetikos wrote:
               | Web development is an example. Fast iteration is the
               | thing that I like. I have so far associated fast
               | iteration with dynamic languages, and it is certainly the
               | case that my experience is that most fast iteration
               | systems are dynamic.
               | 
               | But maybe that simply reflects a concern of the relevant
               | communities. If strongly typed language systems start
               | adding fast iteration approaches to things and are able
               | to achieve a similar level of quick iteration then that
               | will definitely address one of the things I dislike about
               | them. I haven't coded significant amounts of haskell
               | since 2000, but back then what you could do interactively
               | was very restricted.
               | 
               | At the end of the day, the compiler is doing a bunch more
               | stuff in strongly typed languages. It's like taking a
               | bunch of your verification infrastructure and saying
               | 'these must run before you're allowed to see the result
               | of what you wrote'. It will necessarily be slower,
               | although with work maybe it won't be so much slower that
               | it matters.
        
               | tluyben2 wrote:
               | > Fast iteration is the thing that I like
               | 
               | > haskell since 2000,
               | 
               | Things changed a lot in 20 years.
               | 
               | Thanks for noting down something about your age; I have
               | always been a bit ageist about 'fast iteration' as I
               | never met someone close to my age (been devving
               | professionally for 30 years this year) that cares too
               | much about it. I am not a very good programmer, but a
               | very experienced one and i'm consistently faster at
               | delivering than my 'fast iterating younger peers' as I
               | simply know what i'm going to type beforehand, I don't
               | need too many iterations to get it right and I have
               | enough experience to know that i'm close to what we need
               | after it compiles. The people who just type/run 1000
               | times/minute get stuff done, but it's not the way I would
               | ever like (or liked) to work.
               | 
               | > It will necessarily be slower,
               | 
               | GHCi is fast but other avenues can be explored as well,
               | like creating a real interpreter just for development ,
               | like Miri for Rust. Only for faster iteration of logic,
               | you forgo some of the type benefits, but when you are
               | done iterating, you compile and voila. I guess the
               | merging of incremental compilation, jits, interpreters
               | etc will evolve in something that might not run optimally
               | but gives blazingly fast iteration up to perfect
               | performance after deployment. And anything in between.
        
               | tluyben2 wrote:
               | 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.
        
               | steveklabnik wrote:
               | A good post on point six: https://lexi-
               | lambda.github.io/blog/2020/01/19/no-dynamic-typ...
        
               | kybernetikos wrote:
               | > you cannot program without knowing what data you are
               | getting
               | 
               | Types almost always overconstrain. Each part of your code
               | relies on some very specific properties of your data, yet
               | most type systems end up restricting your function to
               | only work with data that meets a whole bunch of other
               | properties that your code doesn't actually care about.
        
               | tluyben2 wrote:
               | > Types almost always overconstrain
               | 
               | Not in my experience; so many, basically, stringy types.
               | 
               | > Each part of your code relies on some very specific
               | properties of your data,
               | 
               | So then you either have a type that exposes those you
               | need or you have different types for different functions.
               | 
               | > yet most type systems end up restricting your function
               | to only work
               | 
               | Again, I don't understand this statement; someone
               | implemented the types to fit the data for some functions
               | they needed. How does the 'type system restrict'
               | anything?
        
         | NicoJuicy wrote:
         | I have the feeling you are applying your experience from coding
         | your own project.
         | 
         | And not from a project in a team, where you didn't code
         | everything.
        
         | petters wrote:
         | IMO types in Python mostly pay for themselves when you develop
         | in a team.
         | 
         | They greatly improve the developer experience by providing
         | automatically enforced documentation. Allows IDEs to provide
         | code completions etc.
        
         | [deleted]
        
         | viraptor wrote:
         | Can you share the tally? I wonder about the categories you
         | used.
        
         | AaronFriel wrote:
         | Given that you don't code with types, how are you certain which
         | bugs you could have prevented?
         | 
         | I'd be curious to see this list. Is it anecdote, anecdata, or
         | is it a spreadsheet you have somewhere?
        
         | benjiweber wrote:
         | I find the value of static typing to be more from increasing
         | the ease of exploration and understanding of a codebase than
         | preventing bugs directly. The constraints on how the code
         | you're reading could be being used making building a mental
         | model faster. Not to mention the tooling built on top of the
         | typing that can help with exploration.
        
           | tluyben2 wrote:
           | > increasing the ease of exploration and understanding of a
           | codebase
           | 
           | Very true and a good thing to remember when discussing static
           | typing.
        
           | bpicolo wrote:
           | Can't be beat for refactoring. In languages I can trust the
           | compiler, I can refactor fearlessly
        
         | Twisol wrote:
         | It's interesting that you talk about bugs, when the OP doesn't
         | make that argument. The OP is very clear, actually:
         | 
         | > It didn't mean I was free from logic bugs. (Nothing can do
         | that in the general case!) It did mean that a program which
         | type-checked wouldn't blow up in ways the type-checker said it
         | shouldn't, though.
         | 
         | Other than soundness (which is not the same as avoiding logic
         | bugs, anyway), the points the OP raises are about
         | _expressiveness_. Types help [you  / the OP] organize more of
         | your knowledge in the codebase. That's never going to be the
         | most obvious preventative measure against bugs, because the
         | goal is higher-order: structuring your codebase so _you_ can
         | reason more clearly about the program.
        
           | TooCreative wrote:
           | That might be a matter of personal taste. How ones brain is
           | wired. For me, types make code less expressive. I can grasp
           | the structure of a piece of code the easier, the less meta
           | data there is on top of the algorithmic structure.
        
             | tluyben2 wrote:
             | What are you working on though? What algorithmic structure?
        
               | TooCreative wrote:
               | 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 such a good grasp on it as
               | with the former.
        
               | unrealhoang wrote:
               | 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.
        
               | TooCreative wrote:
               | 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.
        
               | unrealhoang wrote:
               | Heck, not only I don't have to look at the code, with
               | proper IDE setup, I even don't have to go to the
               | documentation page, just `item.` and my IDE/editor will
               | present me with the list of things that are applicable to
               | my item.
               | 
               | And that's only the response part of the usage, there's
               | absolutely no way for me to know if the method is an
               | enum/constant or is it a string without looking into the
               | code for dynamic typing, and again, sometime N-level deep
               | to get enough information of "what to pass to this
               | function so it will work".
        
               | castwide wrote:
               | > 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.
        
               | TooCreative wrote:
               | For the example I took a random line from Symfony and
               | shortened it a bit:
               | 
               | https://github.com/symfony/http-
               | client/blob/master/HttpClien...
               | 
               | You are free to link to some other example.
        
               | mplanchard wrote:
               | Most type systems also provide information about what's
               | in the array, like Array<String> or int[]
        
               | TooCreative wrote:
               | That is just another piece of metadata. But it does not
               | help either. Now you know you get an array of strings.
               | But you still don't know what is inside of the strings.
               | 
               | It might be ['headers'=>'...','body'=>'...' ] or
               | ['status'=>'...','response'=>'...'] or god knows what.
               | 
               | And you are back to reading the function body.
        
               | mplanchard wrote:
               | Is that like a hashed array you're implying? In that
               | case, the type wouldn't be string anyway, but something
               | like HashMap, Object, Dict, etc. More commonly though,
               | something like HTTP options would be its own type, with
               | defined properties, which you could browse via
               | autocomplete or your IDE's hover/peek functionality. That
               | type would then specify the types of its values, so you
               | can be certain that options.status is an integer, for
               | example.
               | 
               | You'd generally only use an otherwise untyped array of
               | strings when you can't know beforehand what their values
               | can be.
        
               | tluyben2 wrote:
               | 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<Error,Group> 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?
        
               | TooCreative wrote:
               | And now instead of                   $body =
               | Http::request('GET', 'https://example.com')['body'];
               | 
               | You have to do this?                   $body =
               | Http::request(new Method('GET'), new
               | URL('https://example.com'))->body;
               | 
               | And you have cuttered the global namespace with "Method",
               | "URL" and "Option"?
        
               | tluyben2 wrote:
               | Why would they be in the global namespace? They would be
               | in imported namespaces (something\Http for instance).
               | That's rather normal.
               | 
               | And yes, now you have to make sure you are passing the
               | proper and valid types, which is obviously the goal here:
               | $body = Http::request(Method.GET,
               | URL.Parse('https://example.com'))->body;
               | 
               | In other languages you can make it so that the static URL
               | is validated at compile time even.
        
               | jenscow wrote:
               | A better example for a typed function would be something
               | like:                   function Request(string method,
               | string url, RequestOptions options) : Result {...}
               | 
               | or                   Result Request(string method, string
               | url, RequestOptions options) {...}
               | 
               | Here, we know `url` is a string (rather than the parsed
               | object), we know all the supported options (they're
               | members of the RequestOptions object/enum), and every
               | value in the return value. Some might suggest even making
               | `method` an enumeration.
               | 
               | If the function name was better, you could call that
               | function right now without needing any more information.
               | 
               | Also, especially if you're a fan of defensive
               | programming, most of the basic argument checking is
               | performed at compile time, leaving the function body
               | cleaner.
               | 
               | Based on your example alone, perhaps you don't have
               | enough experience with typed languages to appreciate the
               | benefits. I don't mean that in a belittling way, but more
               | of an invitation to learn more about the craft.
        
               | TooCreative wrote:
               | For the example I simply took to a random, recently
               | updated file from Symfony:
               | 
               | https://github.com/symfony/http-
               | client/blob/master/HttpClien...
               | 
               | And shortened it a bit. In the real example, as you can
               | see, there are even more parameters, making it even
               | harder to read.
        
               | jenscow wrote:
               | Well, for me it's easier - perhaps because I'm used to
               | reading parameters in type+name pairs.
               | 
               | The data type is right next to the parameter name - and
               | it will be in the pop-up provided by my IDE when I use
               | the function... I don't need to look at the docs or
               | comment (which may not exist).
        
               | viraptor wrote:
               | You're showing an example of a function signature in a
               | specific language, not an example of typing though. These
               | are related but not exactly the same.
               | 
               | For example here is a Ruby version, very dynamic:
               | def request(method, url, **options)         end
               | request("get", "http...", foo: "bar")
               | 
               | And here is a completely statically typed Crystal
               | version:                   def request(method, url,
               | **options)         end         request("get", "http...",
               | foo: "bar")
               | 
               | The argument types are inferred automatically from the
               | usage and mismatched usage will be caught at compile
               | time. And in the second case the ide can still tell you
               | the types in the signature if you want to know them.
               | 
               | In the third example, here's completely dynamic python:
               | def request(method: str, url: str, **options): Dict[...]
               | 
               | No type checking happens here in the language itself.
               | 
               | So what I'm getting at is - if you don't like languages
               | with verbose explicit type signatures, it doesn't mean
               | you don't like static types.
        
       | andybak wrote:
       | One note of caution. There's a pleasure to solving problems that
       | arise from type systems akin to solving Sodoku puzzles. It's
       | intellectually satisfying and feels like productive work.
       | 
       | I sometime worry that a lot of the overhead and baggage that type
       | systems sneak into coding are hidden because it's fun to resolve
       | them.
       | 
       | I'm still on the fence about types. I love them when they help me
       | but I find myself generally writing more boilerplate and less
       | elegant code than I can sometimes write in Python.
       | 
       | My typed language is C# so I do have type inference (although not
       | the most advanced). I don't have Union types. My IDE does warn me
       | about potential nulls.
       | 
       | So I've got 50-75% of the things listed in this article. And I'm
       | still not 100% sure there's not a hidden cost that's quite hard
       | to define.
        
       | pavel_lishin wrote:
       | A lot of people in the comments are saying how they started off
       | in Java, or C, and hated types, but eventually grew to love them.
       | 
       | I started off in PHP. It was wild. Anything could be anything.
       | Refactoring was a nightmare. Our codebase was littered with
       | mystery variables like $hold, $hold1, and $holda, which were re-
       | used all over the place. (Granted, this two other problems
       | entirely orthogonal to types.)
       | 
       | Then I got a job at a Java place. My god, it was beautiful. I
       | could suddenly _know_ what arguments functions were expecting,
       | even if I hadn 't seen them before. I could be instantly aware if
       | I was trying to use the wrong variable somewhere, or pass in
       | invalid data.
       | 
       | It was as if someone lifted me out of a dank cellar where I had
       | previously subsisted on whatever mushrooms the rats didn't deign
       | to eat, into a brightly lit dining room full of steak and
       | pastries.
        
         | edem wrote:
         | I this is the "profound enlightenment experience" ESR was
         | talking about in his seminal paper "How to become a hacker?".
        
         | eru wrote:
         | Apropos PHP: I hate it with such as much passion as the next
         | guy, but I am quite impressed with what Facebook managed to do
         | with Hack! (Including adding lots of types.)
        
         | temporallobe wrote:
         | Even though I learned C/C++ in school, I started off my career
         | with a typeless language, Perl. I loved it for its simplicity
         | and power to quickly spool up working code, but realized it was
         | problematic to use for large projects for many of the same
         | reasons you state. I then switched to a Java project and was
         | immediately frustrated with types because of how verbose it
         | was, but after a while I came to appreciate just how beautiful
         | and pragmatic it was, especially in its ability to help avoid
         | so many runtime bugs that have been the bane of my existence in
         | the Javascript, Clojure, and Ruby world.
        
           | throwaway894345 wrote:
           | I've often felt that some people dislike types because they
           | expect to be able to write code in a certain way that they
           | know will make some very narrow happy path work _now_ and
           | they get really frustrated when the compiler tells them that
           | there are other paths in the code that don 't work. "Why is
           | this stupid compiler slowing me down?!". This frustration
           | betrays the programmer's indifference toward the broader
           | quality of the project and their willingness to trade bugs in
           | other paths for a feature that appears to be working. The
           | cognitive mismatch is that it's intended change the way you
           | think and program so you can move quickly on many paths at
           | once (not only your very narrow happy path)--with a well-
           | crafted type system, we can move fast _and_ have quality.
           | Note also that  'quality' isn't just about bugs, but also
           | about a code base that is maintainable, similar to the GP's
           | and the parent's observations about the unmaintainability of
           | their PHP and Perl code bases.
        
         | chrismeller wrote:
         | I went a similar path, PHP to C#, and never looked back. When I
         | had to switch to Node for one job I was pulling my hair out
         | constantly (and quite literally) because of stupid things that
         | would never have happened in what I called a "real" language
         | (that being a typed, compiled one). I mean for $deity's sake,
         | there weren't even any dependency injection options at the time
         | and many many times it turned out a bug I introduced was
         | because of simple typo in a camelcase property name.
         | 
         | I absolutely love Node for the ease of writing something quick
         | and dirty. No dependency injection, no coding standards,
         | nothing but a tool to quickly churn through a ton of data or to
         | perform a single task really well. I also think there are some
         | frameworks (using Typescript, like NestJS) that do JavaScript
         | apps really, really well. I will still, never, ever, ever
         | voluntarily write any kind of "real" application in a language
         | that is not type safe again. The benefits just aren't worth the
         | perceived time savings...
        
       | withinboredom wrote:
       | I work on the big data side of Automattic, so I tend to work with
       | PHP, Typescript/JavaScript, Python, and Scala, but I also work
       | with C# for fun. When you work with and without types often, you
       | realize that it doesn't matter. Types are great for expressing
       | constraints on the code, but you can do the same thing with
       | conventions in non-typed code.
       | 
       | IMHO, type-less provides some resiliency. If you write code for a
       | "string" anything that satisfies "stringiness" will still run
       | just fine. This lets you build types that "trick" the code you're
       | calling, which can be useful sometimes. I think I've abused this
       | capability less than 3 times, but more than once. Mostly it
       | allowed me to significantly change behavior without rewriting
       | huge chunks of core code. However, these were all proof of
       | concepts. Don't get it twisted, working with no types is pretty
       | annoying sometimes.
       | 
       | Types, on the other hand, are great for most things, but I do
       | find they get in the way sometimes. It's annoying when you look
       | at the code you're calling and see that it only uses `.x` on a
       | type you pass it, but you have to completely build a type just to
       | create a `.x` on the type you pass in. I once found a NullPointer
       | bug in HDFS with a static type (very strange) and had to work
       | around it doing exactly this. Anyway, I think I'd like Go's
       | approach to interfaces.
        
         | musingsole wrote:
         | Those who argue strongly for one method over the other are
         | likely to not understand the one they're against. Which sucks,
         | because if you do know both, there's rarely a reason to have
         | strong feelings one way or another. So, once again the naive
         | but vocal voices get all the decision power.
         | 
         | /Too many arguments at work are over dogma and I have to spend
         | a lot of my time reminding people that there are options.
         | That's all. There are options. Your kneejerk response to a
         | problem might be fine -- might be right -- but there are always
         | options.
        
           | withinboredom wrote:
           | To expand on that, the options also have trade-offs. They can
           | both be just as right or wrong, and the trade-offs be the
           | deciding factor.
        
           | loup-vaillant wrote:
           | I was never sure what to think of dynamic typing, until I
           | tried to implement a fairly simple Earley parsing algorithm
           | in Lua. The thing was 50 lines, and I was _lost_. I only
           | managed to get runtime errors such as  "you can't add
           | functions, you can't apply a number, this reference is
           | null...
           | 
           | That's when I understood where TDD came from: dynamically
           | typed languages require so many tests to work reliably that
           | we better write those tests first so we're not tempted to
           | omit them.
           | 
           | Anyway, I redid the whole thing in OCaml, and this time got
           | lots of _compile time_ errors. Which I could correct, and
           | once the compiler was happy, well... my program was basically
           | correct.
           | 
           | ---
           | 
           | I still don't rule out that other people's brains are wired
           | fundamentally differently. I don't expect it, it would
           | surprise me, but I honestly don't know. What I do know is
           | that dynamic typing is not for me. I'll suffer through it to
           | get other advantages (Python's comprehensive library for
           | instance), but that's about it. Dynamic typing and I are
           | otherwise _done_.
        
             | withinboredom wrote:
             | > The thing was 50 lines, and I was lost.
             | 
             | The key to writing type-less code is making it so simple
             | that you could cry. Avoid being clever at all costs.
             | However, it's almost the exact opposite when you have a
             | nice compiler checking everything, you can be mighty clever
             | and know that it will work.
             | 
             | I'm lucky in that we can solve a problem in a myriad of
             | languages, depending on if it needs to be "realtime", or
             | can be precomputed. There are some types of problems I'd
             | rather solve in Scala, and others, Python or PHP. It just
             | depends.
        
         | rswail wrote:
         | Using interfaces/traits/protocols don't preclude strong typing.
         | 
         | If you write code for a type that "has" stringiness, as
         | expressed by a trait, then anyone can build a variant of their
         | types that expresses that trait.
        
           | withinboredom wrote:
           | Yeah, for sure. That's certainly possible, but I rarely see
           | people build for it, at least when it comes to value types
           | like strings, ints, etc.
           | 
           | One other thing that I DO like about type-less, is the
           | ability to introspect, for example, in PHP:
           | 
           | ``` foreach ((array) $my_class as $property => $value): ```
           | 
           | vs. a bunch of boilerplate and making sure types match up in
           | most strongly-typed languages.
        
         | mceldeen wrote:
         | > IMHO, type-less provides some resiliency. If you write code
         | for a "string" anything that satisfies "stringiness" will still
         | run just fine.
         | 
         | Things like that are actually possible to do in a _light
         | weight_ way in FP languages and many of them are part of the
         | stdlib. (PureScript happens to be my favorite example.) Many
         | times it brings back the same feelings I had working in Ruby
         | (which I also happen to love), but with the added confidence of
         | a strong type system.
        
       | IshKebab wrote:
       | I don't understand why he thinks Typescript still isn't worth it,
       | when all of his points are issues in JavaScript that are solved
       | by Typescript.
        
       | charliesome wrote:
       | An interesting insight I came across a little while ago is that
       | for mainstream, industrial languages, this way of thinking about
       | types is relatively new. It's not that we're seeing the pendulum
       | swing back to types, it's that we're discovering them for the
       | first time!
       | 
       | In earlier typed languages, the types weren't there for reasons
       | of soundness or productivity at all. The types were there for the
       | compiler alone, as the compiler needed them to know which machine
       | instructions to emit for various operations. Types were just a
       | cost imposed on programmers.
       | 
       | Once computers became powerful enough that we could afford to
       | spend cycles and memory making these decisions at runtime,
       | dynamic languages became viable and we saw industry shift over to
       | them, except in domains where dynamic languages still weren't
       | viable, or where existing codebases or ecosystems made it not
       | economically viable.
       | 
       | Fast forward to the present and decades worth of type theory
       | knowledge is finally filtering through to industry in the form of
       | languages like Rust, TypeScript, Swift, Kotlin, and others. For
       | the very first time we're embracing types for their soundness and
       | productivity benefits. This is an exciting new era.
        
         | bjz_ wrote:
         | As a bit of a nit-pick, it's not _that_ new - see languages
         | like ML, SML, OCaml, Miranda, Haskell, Coq, etc. that combined
         | the notion of types from programming languages and types from
         | mathematics. It's more that it's only recently that _industry_
         | has been learning about it.
         | 
         | That said, I definitely think you're right to point that this
         | is a new thing for industry, and not just a swing back to the
         | idea of types that were previously mainstream in industry. I'm
         | excited too!
        
         | asiachick wrote:
         | I feel like new features of popular typed languages have also
         | helped them catch up to dynamic language it terms of ease of
         | use. Go back before C++11, without "auto" writing generic code
         | sucked! Callbacks without lambda closures also sucked. I'm sure
         | someone will point to some 40 yr old language that had this but
         | those languages weren't popular for whatever reason. var got
         | added to C# in 2007 and it took more releases to let it be used
         | in more places. Apparently added to Java much later.
         | 
         | I'm sure someone will give me a good example but for example
         | std::sort in C++ before closures in C++, if you want to sort
         | one array by another, for example you have an array of indices
         | and an array of values and you want to sort the indices by the
         | values, before closures I'd argue this was fairly painful
         | unless you resorted to global variables or copying all of the
         | data into some intermediate format. You'd end up having to
         | write or generate a class with a sort function solely for the
         | purpose of being able to pass in a member function to sort that
         | could access the values. Today it's trivial because you can
         | write a lambda that closes over the values and pass the indices
         | into sort.
        
         | stult wrote:
         | It seems pretty evident to me that most successful and popular
         | dynamically and statically typed languages are converging from
         | different directions on a similar set of solutions. Very much
         | reflecting the phenomenon you describe. Some simple examples:
         | C# has moved from very strong typing of the exact sort OP
         | criticizes (`Person person = new Person();`) to increasingly
         | permitting looser/more expressive typing with `var`, anonymous
         | types, pattern matching, etc. From the dynamic side, optional,
         | loosely enforced typing is starting to grow more common (e.g.,
         | type hinting in Python, TypeScript in JavaScript) and provides
         | a static but still flexible form of typing. So there's some
         | happy medium where the language balances the permissiveness of
         | dynamic typing and the expressiveness of static typing.
        
           | chrisoverzero wrote:
           | >[...] increasingly permitting looser/more expressive typing
           | with `var`, anonymous types, pattern matching, etc.
           | 
           | None of these features are related to loosening the type
           | system.
        
             | loup-vaillant wrote:
             | It can still feel that way. Take C++ for instance:
             | Foo  f = fooFromElsewhere;  // explicit typing (old)
             | auto f = fooFromElsewhere;  // type inference  (new)
             | 
             | Now what happens if we change the type of
             | `fooFromElsewhere` from `Foo` to `Bar`? With the old way,
             | we need to change the code to:                 Bar f =
             | fooFromElsewhere;
             | 
             | With type inference however, you won't need to change that
             | line at all. And if the new type has enough in common with
             | the old type, you may not change the code at all. It's just
             | as strict as explicit typing, but it's arguably more
             | flexible, and thus _feels_ looser.
        
         | nickmqb wrote:
         | You're not giving the older generations of programmers enough
         | credit here.
         | 
         | While it is true that strong typing is a requirement for the
         | best performance (and this remains so), the productivity
         | benefits of strong typing have been known for a long time.
         | 
         | I mean, just look at languages like C# and Java. These are well
         | established, extremely popular languages, used mostly in
         | business software. A domain where performance is rarely
         | critical. Yet, these languages are very popular. Not in the
         | least because they make it easier for programmers to understand
         | and work with other people's code, and because they provide
         | good tooling, both of which are hugely valuable in a
         | business/enterprise context. Strong typing plays a major role
         | in enabling these features.
         | 
         | Even when C# was still a brand new language, roughly 20 years
         | ago, Visual Studio already provided features like "go to
         | definition", "find references" and autocomplete out of the box.
         | These were a major reason for people to adopt the language.
         | 
         | It's no surprise that people like Anders Hejlsberg, who created
         | C#, later went on to create TypeScript. They already understood
         | the productivity advantages of strong typing and wanted to
         | bring those to the web.
        
       | orobinson wrote:
       | The heuristic I use for choosing a statically typed language vs a
       | dynamically typed language for a task is whether or not the code
       | will sanely fit in a single file.
       | 
       | If that is the case, then that means I'll probably be able to
       | keep the structure of the code in my head and therefore I'll be
       | able to get by with a dynamically typed language. However, once
       | the code starts to span multiple files, typed method signatures
       | in a statically typed language are invaluable. It sucks having to
       | navigate a codebase with tens of thousands of lines of ruby or
       | python code trying to work out exactly what structure of object
       | can be passed to the method you're working on.
        
       | rswail wrote:
       | A lot of people are making comments about how they can code
       | "faster" without types.
       | 
       | But for the majority of the code we write, inital speed isn't
       | that important. Understanding the code and maintaining it are
       | orders of magnitude more important for any non-trivial code.
       | 
       | Types are not only a way for the compiler to understand your code
       | and impose constraints. They're also your _API_ to other
       | programmers. When they see a sum type, they can understand its
       | possible states. When they see a product type they can understand
       | its possible values.
       | 
       | Understanding other people's code is at least half the job of a
       | programmer, whether it's understanding a library or understanding
       | code you have to maintain, or understanding your _own_ code that
       | you wrote 6 months ago.
       | 
       | Types help you do that.
        
         | skohan wrote:
         | > Understanding other people's code is at least half the job of
         | a programmer
         | 
         | This was one of the pain-points when I was working more with
         | node.js: the function signature told you _nothing_ - like
         | whether the function would even return or not would sometimes
         | be a mystery.
         | 
         | In very, _very_ short scripts you can get away without types
         | (like in a notebook for example), but once a project starts to
         | get even medium size the tiny amount of time you put into
         | writing a type name explicitly here and there is more than made
         | up for by the degree it helps with the structure and
         | correctness of your program.
        
           | bo1024 wrote:
           | This kills me about python. So many times I cannot figure out
           | what exactly a functions expects and what it returns,
           | sometimes even from reading the documentation! Matplotlib is
           | especially bad.
        
       | dwaltrip wrote:
       | Off-topic writing feedback: use less italics. Readers are good at
       | figuring which words are important on their own :)
        
       | seanparsons wrote:
       | Types for checking things are correct is really important which
       | is what this talks about.
       | 
       | For me though they really come into their own when those types
       | then help to eliminate boilerplate. Haskell's foldMap function is
       | my classic example of handling the accumulation of a result where
       | it does the hard work based on the types. Idris goes even further
       | by supporting the ability to infer obvious code for your code
       | editor like handling each case in a sum type.
        
       | TurboHaskal wrote:
       | Having started out with Pascal and C, I thought I hated
       | statically typed languages as well until I got exposed to SML and
       | Miranda. I guess Rust or Swift have the same enlightening impact
       | to people coming from Java or Go and I'm glad such approach is
       | finally hitting the spotlight, even if a bit hampered.
       | 
       | That being said, and as much as I see the appeal on those and
       | hoped that stuff like ATS or SPARK were more prevalent, for me
       | they lead to dull, boring code bases. Which is great! But when I
       | look back on my career, the most fun I had, the craziest
       | abstractions, cool hacks, the code I'm most proud of, it's the
       | one written in dynamically-typed on untyped languages. And here
       | I'm talking about some flavor of Assembly, APL, Lisp, Forth or
       | Smalltalk. PHP, Python and JavaScript and similar scripting
       | languages just don't cut it for me.
        
         | rwmj wrote:
         | Yeah the article is just "hey, I discovered SML!". I've been
         | using SML, Haskell, then OCaml since the early 90s and the
         | benefits of (proper) types and type inference have always been
         | obvious.
        
       ___________________________________________________________________
       (page generated 2020-09-27 16:00 UTC)