Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



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...


Node isn't so bad now with typescript. The JS folks learned from endless fixing hot mess code that you couldn't really do with anything less than a good dev experience (Imagine a 5 minute turnaround from edit to test on JS to find a typo in a variable). With TS you get all the nice dev experience from JS, but also some of the C# niceness.


Having spent the last several years working in Node/TypeScript environments (and being firmly in the “I was wrong about types” camp), I think “isn’t so bad” is an overstatement. It certainly isn’t as bad. And especially as TS improves, the possibility to move more and more toward the “not so bad” ideal is there.

But there’s some truly awful stuff in the ecosystem; in the underlying language and the platform’s DNA; in the compromises TS (rightly) makes to be a productive real world tool; in the commonly used tooling; and just peppered throughout everything you can expect to encounter in common third party libraries.

Overcoming all that awfulness requires a lot of additional effort, is inherently limited, and isn’t common in the community (although that too is improving as TS becomes more popular, and as safer patterns become more idiomatic).

I think if I were building a new greenfield project with my choice of platform today, it would be a difficult choice whether to take all that I’ve learned in Node/TS and accept those trade-offs, or to invest in learning another platform.


> or to invest in learning another platform

What other platform do you have in mind?


Honestly haven’t given it a lot of thought, as I’m not currently starting a greenfield project with my pick of platforms.

But I think off the top of my head, languages that I’d look at as first contenders include F#, C#, Kotlin, Swift. I’m sure there are other good choices, for the kind of space I tend to work, that are equally productive and have enough of a community for me to be comfortable adopting them, but I would need to spend some more time researching options to really say with confidence what else I would consider.


"F#, C#, Kotlin, Swift"

All tied in with one singular company each that calls all the shots - For good and for bad. You kinda get all their other stuff showed down your throat along with the languages and things can get bloated fast. But at least things are streamlined. Nodejs had an uprising with io.js in 2014-15 and is now governed by a foundation. I like that a lot. I sleep soundly knowing that no company can pull the rug from under me on a whim.


At least C# (and Swift too) is pivotal to that one big company offering. Free software and openness are important but they are not a silver bullet against bad management.


If most business-side people actually knew what weak typed languages meant for their company, I can't imagine so many of them going for php/js as often as they do. You're always one expression with a `$contact` instead of a `$contract` in it away from a cancelled weekend trip or humiliating sales demo.


If the business side is making these kinds of technology choices, the business itself and the engineering side both have significantly worse and more important problems. And those problems will inevitably create those cancelled weekends.


Honestly, I think there’s an equivalency there. Someone who is an expert in JavaScript means a couple of years. You can hire a high-end JS Dev for a lot cheaper than you could any other language or platform.


The other issue with dynamically-typed languages is that the language sometimes "helpfully" fixes the types for you. I came across one JS project that said this:

  for (var i = 1; i != Math.pow(2, 16); i <<= 1)
Replacing Math.pow(2, 16) with 1 << 16 effected a 30× speedup--and this was in SpiderMonkey, which tends to be a little less finicky about optimizing mixed types than V8.

(It's been a while since I coded PHP, but my recollection is that PHP tries to pull a strings-are-integer tricks a few times).


> my recollection is that PHP tries to pull a strings-are-integer tricks a few times

Yeah, the implicit casting in PHP is bonkers, especially when it comes to comparisons. The comparison table looks like the scribblings of a madman: https://www.php.net/manual/en/types.comparisons.php

(though granted, Javascript acts very similarly.)


I have no hate for PHP, it’s been a tool in my toolbox for over a decade, I think it has a lot of good qualities, and it has made me a lot of money.

That said, after ten years of use, I still see something like:

    function_returning_false_or_int() > -1
And sometimes have the wrong intuition about what it does.

Now, nobody should write that shit in the first place, but turns out it’s equivalent to ‘!== false’.

‘(int)false’ is zero, but ‘false > -1’... is not true.


This doesn't look to me like a dynamic typing problem. All the types are Javascript numbers. The issue probably stems from Math.pow's flexibility; it can even accept fractional exponents. The more general algorithm is probably slower.


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.


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.


Yea, I agree and for this reason when I teach type systems to new programming students, I tell them that a type is somewhat analogous to a building material. You have brick, wood, steel and iron. You want to build a house that has solid bedrock and has easy forest fires in the area. What material would you use? Most would say rock.

Then I tell them about strings and ints. You can represent 66 both with strings and ints, but one is usable for computation where the other one is simply usable to write Alice in Wonderland with it, or simpler forms of text. Sometimes I get the question then: why can't a string both be computational and as a means of character display? And then I list the upsides and downsides of such a system, just like the upsides and downsides of mixing brick and wood to build the outer wall of a house.

I'm curious if people can think of other analogies that they use for teaching.


Why do you need an analogy at all? Are your students deeply familiar with the construction characteristics of wood and brick already and ready to draw parallels to software? How do those things relate at all to the subject at hand, strings as “means of display” (data?) versus strings as “computational” (keywords, operators, etc?).

Personally, I avoid analogies because they usually mean I don’t really know how to teach the topic, and I’m “hand-waving” on the fly. I either find a way to build on the student’s existing knowledge or I “park” the topic for discussion when the student has enough knowledge to give a correct answer.


I appreciate analogies when I start learning something. Although it's not a perfect representation of what's being taught, it makes it easier for me to start thinking about the topic I'm learning. I can fill in the details later. I think it's useful for certain types of learners such as myself. Although I'm not deeply familiar with the characteristics of construction materials, I was more familiar with them compared to types when I was learning about them. So it would've been a helpful analogy for someone like me.


I don’t know if this is pedagogically useful, but I think of types as shapes, variables (including struct fields and function parameters) as a shaped hole, and values as a shaped thing that can fit (or not fit) into those holes. Kind of like the children’s toy. You don’t want to pass a circle in to a function that needs a square shaped thing, and the type system helps make sure you don’t do that by accident.


If we think of variables as boxes and types as label on those boxes, does it makes sense to have labels on the boxes if complexity of opening the box just to see what is inside goes up?


I think types also get in the way of writing speculative code, which is something I did a lot more often when I was new.

Run what you have so far. Print the last value you computed. Crash. Verify the data has the "shape" you expect it to. Write a bit more code. Repeat.


Probably a little. I might lack average cognitive facilities, but I find that types pay off for me very early. When I worked in a Python shop, I would prototype in Go because the types helped me move fast, and then I could port it to Python (often for a huge performance loss, not to speak of maintainability) to integrate with the code base.

On the other hand, Python’s repl was nice for a small handful of tasks (although I mostly used it for figuring out what the actual type of some variable was, which is obviously a non-problem in the statically typed world).


In the Clojure world we use clj-kondo, maps, and spec to solve these problems

We get editor time feedback of mistakes with optional type hints + light inferance via clj-kondo and a data modelling system that we can export out as database schema, JSON schema etc

And dynamic enough constraint system to express something like all human names must be "Tony" only on Tuesdays

The same constraint that can be shared server side and client side without writing it twice without learning more syntax

Additionally check what the expected inputs for a function are by checking the function specs I think guardrails pro will make this more ergonomic when released

And finally ask the constraint system to generate valid examples of the constraints great for mocking data

I don't miss type systems but I also understand if you're not using solutions here then you're in trouble


Ya, that's why I think each language kind of benefit to different levels of having static type checkers and of various features as well. You can't just blanket say all type checkers are bad, or all language without one are bad.

Clojure is a good example here, it actually can be used with a very powerful static type checker core.typed, yet its users chose not too for reasons that say in JavaScript maybe a different choice would have been made. The REPL for example catches lots of type errors as you code. Other languages don't have such a coding workflow, so a static type checker feels really great in that it too will catch type errors as you code, etc.


Does anyone still use core.typed? My impression was that Circle CI's article announcing that it was moving away from core.typed was effectively a death blow to its community. One of Circle CI's founders later went on to use OCaml instead of Clojure on his next project (Dark) precisely for its static type system.

None of that is to say OCaml is "superior" to Clojure in some way. I disagree with a lot of the ways that the OCaml type system has evolved and I wouldn't be surprised to see people who have moved to Clojure from OCaml. However, having programmed professionally in Clojure (although it's been quite a few years so I'm not familiar with the latest advancements in e.g. spec) I still think a Clojure-like language could benefit from a static type system.

I don't think it'll work for Clojure itself because of a variety of patterns and choices in the standard library (which is in part why I think core.typed died, we also found core.typed painful to use in some of our experiments at my old job both in how it interacted with Clojure and the tooling around it). And philosophically Rich Hickey would probably kill Clojure before he ever considered designing Clojure around a static type system. However a programming language based off the same data driven ideas could maybe do it.

While a REPL and hot code reloading are absolutely huge productivity boosts, they are more or less orthogonal to the benefits provided by a good static type system (see e.g. hot code reloading with Elm or Purescript which comes quite close).

The thing that static type systems provide over tests and runtime contracts is the ability to constrain users of the API of a library. We use regression tests to make sure regressions in code we write doesn't happen again. Likewise types are effectively regression tests at the API level to make sure certain regressions in code that calls our code doesn't happen again. That is an extremely powerful capability that I consistently miss in dynamically typed languages.


The thing is, core.typed is a really powerful type system, but the ergonomics with the way Clojure works didn't work out, so people prefer not to use it when developing with Clojure.

That's what I find interesting about it. Not all language benefit from a type system in the same ways, some, like Clojure, actually get crippled. Now, it can mean that you need to find the right kind of type checker that provides the correct ergonomics for Clojure and maybe that would work. But it's still quite interesting.

For example, Erlang has a bit of a similar thing, Dyalizer made specific choices to work within Erlang's design. Had it not done so, it probably wouldn't have found adoption. Same with TypeScript.

So what's interesting here is that you have an apples to apples comparison where a language is found to be better without the constraints of static type checking.

When you look at other statically typed languages, they're oftened designed around the static type checker. That's the main focus, and the language itself revolves around that. So obviously in such a language, the type checker would be a necessity, as it's the main draw. So it's interesting to look at Clojure for a counter example.

That said, JavaScript you could argue is also an apples to apples comparison, and people opted for types. It'll be interesting to see also where Ruby and Python go, now that they have type checking features as well.


I still can feel very lost when refactoring a complex clojure system as opposed to something like Rust, because you have very little information about all the places in your codebase where a certain assumption was made.

You can go crazy and spec everything, in fact that can help, but:

- In practise, nobody does it

- Specs come with no guarantees. They could even be wrong.

- The official implementation stubbornly insists on not checking return types, so half of your annotation may just be glorified documentation (although you can use third party libs like orchestra)

Just imagine: Add a new required field to a spec, and get a convenient list of source code locations that you need to review. That's the promise of a statically checked system. It's not a silver bullet, but not having this leads to what I like calling "refactor anxiety" (i.e.: did I handle all cases?)

I still love clojure no matter what. I think in practise you can express so much, so elegantly, and with far less code, that your project size is always sorta manageable.


You don’t miss type systems, because you’re using something very similar.


I did Java dev for a while, after being C before then. I felt like everything needed a pile of typecasting in front of it to work, even though these were often objects for classes you'd think should all play nicely. I realized only after dealing with it for so long that Java wasn't supposed to work that way, but how things can work when well written, vs old apps where half the code is written by a long line of 4-month co-op students, are two very different things.

Ultimately I think people just weren't given any credit for making good, reusable types, because then the next dev who submits a better feature faster using your work gets a raise, but you look like a kook ranting about best practices who doesn't do anything "business".


Type systems limit the set of programs accepted by the compiler. A sound type system will reject every bad program - but also may reject some good programs. Type systems therefore also will have escape hatches to let the programmer overrule the compiler.

Bad type systems need you to use the escape hatches frequently - you can't write much C without using casts.

I haven't yet used a language with no need at all for an escape hatch - but some languages need them far less often than others.

Java's type system is better than it was. The main places it still has weaknesses are around exceptions (you will need to wrap checked exceptions in runtime exceptions in places you'd have preferred to specify an exception type via generics, eg) and the occasional cast after an instance type check.

You can phrase good development practices in business terms: what are the risks to the business due to sloppy code? Are they greater than the risk of being slow to market?

Sloppy code has accumulating costs. A good type system can help greatly with refactoring to address those costs, but it cannot help with the attitude that those costs aren't real and don't need to be paid.


> I started off in PHP. It was wild.

Help a lot to start with something SO wrong :)

My first dynamic language was python, and the quality of the docs and overall apis of major libraries soft the landing.

But later I note that was parts of MY code that become a mess. The problem of delay of good design? is that good design GETS delayed.

And then fix that later is a problem. Major libraries and core APIs have the time frame and focus to polish them, but the rest of the code most do?, not much, so it stay in the awkward phase of "later will be refactored, maybe. Perhaps..."

Then later I move to F# and rust and can't delay bad design for long.

Is a chore to slow down at the start of the coding phase but later the speed up is huge: i don't need to fix my past mistakes by the truckloads...


My personal favourite omnipresent php vars are `$data` and `$value`. Like, no shit, what else are you storing in variables?


Early on at the company I'm at now they decided to exclusively use $params for param passing to functions as the one true way to do things. It supports easy default params, named params, an easy passing of config through to deeper layers without needing to know about it at intermediate layers.

What it doesn't support is any sort of readability as a code base scales. It's often hard to know how the function you're calling will behave because the param can tweak some flag and totally change the behavior.


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.)


Don't forget, much of that effort has now trickled down into the PHP language itself. We've long had scalar types for function parameters and return values, but with 7.4 and 8.0 we've added union types[1], nullable types[2], and typed properties[3]. While not a part of the language yet, generics can be annotated with the linting tools PHPStan and Psalm[4], and native support for these annotations is coming to the next release of PhpStorm[5].

Using PhpStorm, I've worked with 100 kloc PHP codebases where almost every type was inspectable. And the language support and tooling are only getting better all the time.

1: https://stitcher.io/blog/new-in-php-8

2: https://www.php.net/manual/en/migration71.new-features.php

3: https://stitcher.io/blog/typed-properties-in-php-74

4: https://phpstan.org/blog/generics-in-php-using-phpdocs

5: https://blog.jetbrains.com/phpstorm/2020/07/phpstan-and-psal...


Coming from Haskell and Scheme, I'm now waiting for them to unify the syntactic handling of variables.

At the moment, you have to put a $ in front of most variables, and no adornment for when you want to call something as a function. Reminds me of Common Lisp with its two name spaces for functions and other variables.


Yeah, the $ sigil for variables is a historical curiosity, but I don’t think it’s going anywhere. It would be an unacceptable BC break for codebases that have implicitly relied on this “two namespaces” behavior


Alas, yes. And it doesn't cause any real problems apart from aesthetics.


I had almost the same experience, first learning Basic and Fortran, then coming to Pascal and C. The difference is, I just was a student. I didn't write anything big enough to really get the need for types. For a couple hundred lines, that only had to live for maybe a week, who needs types? So when C and Pascal demanded them, I was annoyed.

A few decades later, I really value them. My code now lives for decades, not for weeks. Lines are measured in the hundreds of thousands. Other people work with me. Types eliminate a set of errors. They're worth the effort.

I think time is where the tradeoff lies. How long are you writing for? If your code is only for this week, types maybe are a net loss. If you're writing for a month, maybe it's a toss-up. But if you're writing for a year, types are a net win.


I used JavaScript for years and then used C# for years. During all those years, I also use languages with advanced type systems like TypeScript, Scala, Idris, etc.

I still hate typing in Java / C#. I feel intellectually insulted from time to time. There are too many times I can naturally express something in natural languages or just JavaScript, but cannot easily express those in Java or C#.

TypeScript is great, it lets me express many things I want, but it's more or less bloating and inconsistent. I always have to worry if the signatures are hard to read for the team members.

Interestingly, TypeScript reveals a lot of use cases to the mass - there's more to type systems. It's not possible to write a typesafe 'printf' in most of the statically typed languages. Maybe a full-sized dependent typed language like Idris will come in sight in the following decades.


I been jumping between Java, ruby, Javascript for some years now.

Ruby flexibility is amazing. I love the language and syntax frameworks built around it. Amazing.

Except for investigating and refactoring. Everything in Java, even in worse written systems, was fairly easy to understand and find using intellij and friends. Meanwhile in ruby if someone gets messy it might be impossible to figure that code out.

I like the concept of typescript best. Types when you want them. No types when you just prototyping. It was actually my favorite aspect of Adobe flex when I had a short stint coding in it 12 years ago.


I started with C and some Pascal. Then Java and Perl. Then JavaScript, then Ruby, then Python, then Elixir.

The great thing of Java was garbage collection. No more malloc/free hell and programs still worked.

The great thing about Perl and the other scripting languages was no type declaration (plus garbage collection.) And programs still worked.

After 30 years I kind of agree with the author of the OP: the time lost annotating programs with types of not offset by the benefits. In almost all cases type discovery is something the computer should do, not me. Same as for memory management: automatic, not manual.


My second job had a lot of VERY old C code. They were viciously strict about enforcing Hungarian notation for variable names. This is a workaround for your concerns, but I prefer types any day.


I also started off with PHP, about 20 years ago. Soon after, I started working with C# and had the same feelings you had about Java - code was so much more readable, maintainable and refactorable, and so much less buggy. I've never looked back from static typing, and am put off from using languages I'd otherwise be interested in (like Elixir).


I had a love/hate relationship with types for a long time.

I loved them because, well the things you mentioned.

But I hated them because in C++ it was required to open a separate header file to declare them.


I this is the "profound enlightenment experience" ESR was talking about in his seminal paper "How to become a hacker?".




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: