News for hackers. Stuff that matters.
Re:Ask HN: Who regrets choosing Elixir? by alexbanks on Saturday May 23, 2020 @12:45PM (#23284379)
I see that language parroted all the time - "With thorough enough testing a dynamic language shouldn't be a problem", and I have never understood it. Arguing to build what is essentially a build-time type checker in the form of automated tests seems twice as cumbersome for half the benefit. Instead of building tests that check each branch of a program's types, why not use a language that forbids dynamic typing? You should still have tests, but IMO tests that are just checking that a string is a string are A.) Time consuming, and B.) largely useless beyond type validation.
Re:Ask HN: Who regrets choosing Elixir? by smoe on Saturday May 23, 2020 @01:27PM (#23284739)
Not GP, but I have done plenty of TDD development in dynamic languages and not once have I written a test that checks whether a string is a string. You get that implicitly because others test will fail if the types don't match.

While I personally prefer certain statically typed languages over dynamic ones for new projects, In practice, for small to medium sized projects, runtime type errors is in my opinion much less of an issue as some people make it out to be. Except for null exceptions they rarely make it into production and if, are easy to track down and fix.

Interestingly, a lot of the people I have seen constantly running into type problems in say Python, are the ones coming from statically typed languages that keep insisting doing thing the way they are used to instead of embracing the duck.

Re:Ask HN: Who regrets choosing Elixir? by pavel_lishin on Saturday May 23, 2020 @03:19PM (#23285591)
> I see that language parroted all the time - "With thorough enough testing a dynamic language shouldn't be a problem", and I have never understood it.

"If you drive carefully enough, a car without seatbelts shouldn't be a problem!"

Re:Ask HN: Who regrets choosing Elixir? by alexbanks on Saturday May 23, 2020 @03:33PM (#23285693)
Exactly
Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Sunday May 24, 2020 @10:03AM (#23291575)
Analogies are a great way to explain things, but not so much a great way to prove things.

If you're writing software that's life and death critical like wearing a seatbelt, you should absolutely be using a strong, statically typed language, because catching errors at runtime is completely unacceptable. But incidentally, none of the proponents of static types on this thread have talked about any languages that I would actually use for this situation. Java or C-family languages certainly aren't strongly typed enough. Type systems aren't a magic bullet.

But in the vast majority of modern software, it mostly just matters that you catch and fix bugs quickly--whether you catch those bugs at compile time or runtime is usually not as critical.

Re:Ask HN: Who regrets choosing Elixir? by pavel_lishin on Monday May 25, 2020 @05:12PM (#23305041)
> Analogies are a great way to explain things, but not so much a great way to prove things.

What are we proponents of static types being asked to prove?

> But in the vast majority of modern software, it mostly just matters that you catch and fix bugs quickly--whether you catch those bugs at compile time or runtime is usually not as critical.

I would argue that catching bugs at compile time, before you ship them, is vastly preferable to catching them at run time.

Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Monday May 25, 2020 @11:53PM (#23307257)
> What are we proponents of static types being asked to prove?

Your arguments for why you think static types are better.

> I would argue that catching bugs at compile time, before you ship them, is vastly preferable to catching them at run time.

I would argue that you're only doing the benefit part of a cost-benefit analysis, which isn't very useful.

Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Saturday May 23, 2020 @12:56PM (#23284483)
> You should still have tests, but IMO tests that are just checking that a string is a string are

The correct completion to this sentence is "irrelevant.", because that's not what anyone is proposing.

The fact is, behavioral tests catch a lot of type errors even without intending to, and more to the point, if you test all the behavior you care about, then you don't care if there are type errors, because they only occur in situations where you don't care.

Re:Ask HN: Who regrets choosing Elixir? by jfim on Saturday May 23, 2020 @02:05PM (#23285016)
Having static type checking avoids errors caused by typos, missing match branches, and other brain fart-style mistakes. For example, in Elixir:

  f = fn
    {:ok, message} -> "It worked #{message}"
    {:eror, message} -> "There was an error #{message}"
  end
Running this

  iex(2)> f.({:ok, "yay"})
  "It worked yay"
  iex(3)> f.({:error, "oh no"})
  ** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/1
This kind of error isn't caught if all of the testing doesn't cover the error paths.

Contrast with Scala, where using Either (which can be Left or Right):

  def f(x: Either[String, String]) = x match {
      case Right(x) => s"It worked $x"
      case Left(x) => s"There was an error $x"
    }
If for example one forgets a branch:

  scala> def f(x: Either[String, String]) = x match {
       |     case Right(x) => s"It worked $x"
       |   }
                                          ^
         warning: match may not be exhaustive.
         It would fail on the following input: Left(_)
Or to follow the Elixir tuple pattern more closely:

  scala> sealed trait Status
       | case object Ok extends Status
       | case object Error extends Status
  trait Status
  object Ok
  object Error
  
  scala> def f2(x: (Status, String)) = x match {
       |     case (Ok, msg: String) => s"It worked $msg"
       |     case (Error, msg: String) => s"There was an error $msg"
       |   }
  def f2(x: (Status, String)): String
  
  scala> def f2(x: (Status, String)) = x match {
       |     case (Ok, msg: String) => s"It worked $msg"
       |   }
                                       ^
         warning: match may not be exhaustive.
         It would fail on the following input: (Error, _)
  def f2(x: (Status, String)): String
The typo also gives an obvious type error:

  scala> def f2(x: (Status, String)) = x match {
       |     case (Ok, msg: String) => s"It worked $msg"
       |     case (Eror, msg: String) => s"There was an error $msg"
       |   }
             case (Eror, msg: String) => s"There was an error $msg"
                   ^
  On line 3: error: not found: value Eror
Caveat: still learning Elixir and my Scala is rusty, so there might be better ways of doing the above. :)
Re:Ask HN: Who regrets choosing Elixir? by pavel_lishin on Saturday May 23, 2020 @03:20PM (#23285599)
> if you test all the behavior you care about, then you don't care if there are type errors, because they only occur in situations where you don't care.

If I test all the situations I care about, the one situation I thought I didn't care about is going to fuck me in production.

Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Sunday May 24, 2020 @12:25PM (#23292584)
If you test all the situations you care about and statically type check, the one situation you thought you didn't care about and that wasn't caught by the type checker is going to fuck you in production.

It's not useful to talk about a binary "bugs versus no bugs", because "no bugs" isn't plausible in most codebases.

It's also not useful to talk about "more bugs versus fewer bugs" because that's only part of the picture: the other parts of the picture are how much development effort was necessary to achieve the level of bugs you have, and whether the number of bugs you have, and when you have them, is acceptable.

If it's a life or death application where any bugs at runtime are unacceptable, then of course we want static types, but static types aren't enough: I'd also want a theorem prover, fuzzer, a large number of human testers, and a bunch of other things that require way too much effort to be useful in an average software project.

The vast majority of software projects, runtime bugs are acceptable as long as they don't lose data, cause downtime, or expose private information. If you catch these bugs during unit testing instead of 30 seconds earlier at compile time, that's fine. Static types might catch a few more bugs, but it is very much not in evidence that the level of effort involved is lower than equivalent unit testing in situations where reliability requirements are typical.

Re:Ask HN: Who regrets choosing Elixir? by _t0du on Saturday May 23, 2020 @01:46PM (#23284884)
My personal real life experience with Ruby developers disagrees with you, but I accept that I could just have experienced a set of developers that weren't very good at testing.
Re:Ask HN: Who regrets choosing Elixir? by peferron on Saturday May 23, 2020 @06:24PM (#23287107)
Trust your experience.

It's impossible to defeat the claim that "with good tests you don't need static type checking", since every counter-example can be dismissed by arguing that better developers would have covered that test case.

I can claim just the same that "with correct code you don't need tests", and dismiss every counter-example by arguing that better developers wouldn't have made that mistake.

Obviously we know that developers sometimes make mistakes, and sometimes these mistakes are in the tests themselves. So trust your experience regarding how this all works out.

Re:Ask HN: Who regrets choosing Elixir? by _t0du on Saturday May 23, 2020 @06:39PM (#23287222)
Yeah. You formulated my opinion much more elegantly than I could've - in my experience it's always the "Well if the tests didn't catch this, we just aren't testing enough." Which in my experience is a losing strategy, you'll never test "enough" in languages like Ruby.

In my experience this idea always brings a strawman, "Well in statically typed languages you still have to test", which is obviously true. But the type of tests and the content of the tests is very different.

Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Sunday May 24, 2020 @09:52AM (#23291508)
> Yeah. You formulated my opinion much more elegantly than I could've - in my experience it's always the "Well if the tests didn't catch this, we just aren't testing enough." Which in my experience is a losing strategy, you'll never test "enough" in languages like Ruby.

Maybe someone is saying that, but I didn't say that.

My question isn't whether you can test enough to catch all bugs. My question is whether time spent wrangling types gets you more value than time spent writing tests.

> But the type of tests and the content of the tests is very different.

Are they? How so?

Again, unit tests of the form `assert isinstance(foo, type)` are an antipattern--that's not what I'm proposing.

Re:Ask HN: Who regrets choosing Elixir? by alexbanks on Sunday May 24, 2020 @01:19PM (#23292981)
> My question isn't whether you can test enough to catch all bugs. My question is whether time spent wrangling types gets you more value than time spent writing tests.

Yes, by a tremendous amount, in my experience.

> Are they? How so?

Tests are only as good as the person writing them. I could see a model working where the person that wrote the code isn't the person that writes the test, but that's definitely not how most development orgs work. If a dev is good enough/capable of writing comprehensive enough tests to accurately test the correctness of their code, that's great, but almost none are (I say almost because I actually mean "actually none" but am leaving room for my own error). If you're an average dev, you'll write average tests (neither of these are insults), but that means you still won't catch everything (by a lot).

I think another comment of yours on my posts actually summarizes the core disconnect between your thinking and mine.

> But in the vast majority of modern software, it mostly just matters that you catch and fix bugs quickly--whether you catch those bugs at compile time or runtime is usually not as critical.

I couldn't possibly disagree more with this statement.

Re:Ask HN: Who regrets choosing Elixir? by kerkeslager on Tuesday May 26, 2020 @12:28AM (#23307420)
> > My question isn't whether you can test enough to catch all bugs. My question is whether time spent wrangling types gets you more value than time spent writing tests.

> Yes, by a tremendous amount, in my experience.

Well, then I'd have to ask what your experience is that causes you to believe this? I don't mean years, I mean what languages, and what, more specifically, you observed.

> Tests are only as good as the person writing them. I could see a model working where the person that wrote the code isn't the person that writes the test, but that's definitely not how most development orgs work. If a dev is good enough/capable of writing comprehensive enough tests to accurately test the correctness of their code, that's great, but almost none are (I say almost because I actually mean "actually none" but am leaving room for my own error).

Static types are also only as good as the person writing them. C's type system, for example, lets through a wide variety of type errors. And users of a type system can easily bypass a type system or extend it poorly: I've written a lot of C#, and while C# has a type system which, when effectively used, can be extremely effective, I've also seen it used with dependency injection to cause all sorts of tricky bugs.

I don't think we can conclude much from bad programmers doing bad things except that good programmers are better, which is practically tautological.

> If you're an average dev, you'll write average tests (neither of these are insults), but that means you still won't catch everything (by a lot).

So? "Catching everything" isn't a thing--if you're talking about that, you're not talking about reality. There are two systems I've ever heard of which might not have any bugs--and in both cases an absurd amount of effort was put into verification (far beyond static types), which, even late in the process, still caught a few bugs. Static types aren't adequate to catch everything either.

> > But in the vast majority of modern software, it mostly just matters that you catch and fix bugs quickly--whether you catch those bugs at compile time or runtime is usually not as critical.

> I couldn't possibly disagree more with this statement.

shrug Okay... To be clear, "runtime" doesn't mean "in production".

Re:Ask HN: Who regrets choosing Elixir? by alexbanks on Tuesday May 26, 2020 @06:54PM (#23317419)
> Well, then I'd have to ask what your experience is that causes you to believe this? I don't mean years, I mean what languages, and what, more specifically, you observed.

Probably 20 Rails developers across 3 companies

> So? "Catching everything" isn't a thing

The "everything" I'm referring to is less about "all bugs", and more about "Type errors, spelling mistakes, missing imports, etc". All code has bugs. Not all languages allow remedial spelling errors to make it into a build.

> shrug Okay... To be clear, "runtime" doesn't mean "in production".

It sure does not. I'm not sure that changes anything. It seems like being difficult for the sake of it to argue that it's the same value to catch potential bugs now vs. later. Obviously the answer is now.

Re:Ask HN: Who regrets choosing Elixir? by bryanrasmussen on Saturday May 23, 2020 @01:04PM (#23284548)
type errors are generally the lowest common denominator error, thus tests that catch higher level errors will also catch whatever typing error is related to the actual error you are testing for.

Not to say I haven't had benefits from catching type errors but generally not as great as those I have from having an automated GUI test running.