Live data from Hacker News

Ask HN: Who regrets choosing Elixir?

news.ycombinator.com

81–90 of 340 posts

Re: Ask HN: Who regrets choosing Elixir?

#81
post #31

Earlier quoted context omitted.

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 s…

> 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 occ…

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?

#82
post #73

Earlier quoted context omitted.

This still produces a runtime error, to make code maintainable you want to be able to check the types without having to run the program. The type annotations help with that, they allow to spot bugs in the code and make an IDE to correctly refactor code and provide autocomplete.

Type checks don’t actually enforce anything though, and as far as I can tell don’t even produce a warning in vscode unless everything is typed (a typed var fed into a typed function signature).

Correct, the annotations in python don't enforce anything, although there are packages that make them produce errors, but again, those errors are at runtime.

You can run mypy which will list problems. Also PyCharm understands the annotations and does all the things I mentioned it do: proper autocomplete, proper refactoring functionality and highlighting errors

Re: Ask HN: Who regrets choosing Elixir?

#83
post #71
post #40

Earlier quoted context omitted.

Erm, you can setup dialyzer to be as strict as possible. This might not catch all the errors because of the nature of messages, but this is a pretty small part of a system usually and can be covered by tests. >All in all it's not a useful substitute for a real type system used to model things. >Type specs aren't useful for domain modelling Can you give me some examples?

> Erm, you can setup dialyzer to be as strict as possible. No. This is just plain false. Having used dialyzer since 2016 I can tell you it's not useful as a substitute for statically checked types via a compiler, with real strictness (which includes "When I don't have enough info, that's a type error"). > This might not catch all the errors because of the nature of messages, but this is a pretty small part of a syste…

>They're ad-hoc, serve as documentation at best and even if they had structural power enough to express basic things they aren't checked at all

What they are is how you use them, can you show how you can model something with static types and how it is different from dialyzer?

Re: Ask HN: Who regrets choosing Elixir?

#84
post #66

Earlier quoted context omitted.

Modeling your domain using types and checking your types are two different things. Could you explain where you think the Elixir type system falls short for modeling domains? Your post only gives examples of problems with type checking.

Elixir doesn't have any concept of real tagged unions or sum types. Modelling is intimately connected to checking because when we change something (as we are wont to do) we want to have that change bubble out into the rest of the system safely. This just isn't reliable at all in Elixir. The most potent data modelling tool you have in Elixir is a `struct`, which is just a `Map` with `atom`s for keys. You can then asso…

>Elixir doesn't have any concept of real tagged unions or sum types

What about this?

  -type int_or_str() :: {a, integer()} | {b, string()}.

Re: Ask HN: Who regrets choosing Elixir?

#85

Earlier quoted context omitted.

When people refer to "typing", it is almost certainly reasonable to assume they are referring to static typing, as implemented by most languages. I don't think this should be confusing. Specs/contracts are cool but ultimately don't afford the same kind of descriptive and expressive power that a static type system does. > static types in most languages[2] don't reduce this burden much: there isn't an alternative to th…

> When people refer to "typing", it is almost certainly reasonable to assume they are referring to static typing, as implemented by most languages. I don't think that's a reasonable assumption at all. Even if, as you assert, the average person doesn't understand that types exist in dynamically-typed languages, I don't think that means I have to conform to common misconceptions. > Specs/contracts are cool but ultimate…

> Could you explain what descriptive and expressive power is missing here?

tldr: i haven't seen a runtime typechecker that handles generics and function types in a satisfactory manner.

i've used various Python libraries for runtime type-checking (based on `typing` annotations) like `typeguard`. and they work okay for simple types, but suck for anything involving generics and function types. checking if something is a `List[int]` every time it's passed as a parameter is too expensive, because you have to go through the whole list (and you're out of luck if it's an Iterator[int] - can't traverse that without exhausting it). runtime typecheckers don't have enough information to check if something is a valid `CustomList[int]`. and they have no way of checking if e.g. a function (passed as a parameter) is actually a `str -> int`, at best you'll find out when you call it.

and runtime checkers, at least the ones i've used, often end up requiring more annotations than i'd have to write in a type-inferred language. it might be possible to work around that to some degree, but I think that's a fundamental limitation – unlike a static checker, they only have info about code that already ran. so you'll have to annotate code like this:

  f xs = cons 'a' xs
because a runtime checker can't "look into the future" and tell that based on the usage of `cons`, the only sensible type for `xs` is List[Char], so `f` must be of type `List[Char] -> List[Char]`.

Re: Ask HN: Who regrets choosing Elixir?

#86
post #6

Earlier quoted context omitted.

An extra point is that finding Elixir developers (or people who are willing to learn it) is also much harder.

I have heard this quite a bit, and it very well may be true, but it has not been my experience. I worked in a rails shop and there are tons of people that want to learn and work with elixir, but there are no jobs they can find. Likewise on Elixir forums and slack, there are lots of people looking for Elixir work, but not many people hiring for it.

I think within the rails community there's enough people that want to give elixir and phoenix a try but if you're not using phoenix the application pool drastically shrinks.

Re: Ask HN: Who regrets choosing Elixir?

#87
post #46
post #7

I've done a fair bit of both Ruby and Elixir. My impression is that Elixir leaves a lot of the legacy cruft behind, and it has a much smaller language feature set (which is IMO big bonus). The language is pretty easy to grasp quickly as a result. There isn't much in the way of quirky syntax or backward compatibility weirdness. Probably the biggest advantage of Elixir over Ruby is the runtime. The Erlang VM has proper…

>somewhat slow runtime (compared to C, Java, Rust, Go, etc). It's only slow if you are comparing a single-threaded operations, Erlang VM is designed to scale horizontally, not to be fast with one thread.

Elixir and Go both have similar performance (although Elixir tends to use a lot more CPU). Here's a pretty good blog post comparing Go, Node and Elixir: https://stressgrid.com/blog/benchmarking_go_vs_node_vs_elixi...

Re: Ask HN: Who regrets choosing Elixir?

#88
post #80

I work for a Ruby on Rails shop and we used Elixir for two projects about a year or two ago when it was getting a lot of good press. The first project was an API that was intended to serve as a middleman between a few legacy services. Basically the company that hired us was building a new JSON API but didn't want to rewrite all their old code and our job was to consume the output from their ugly legacy APIs and produ…

> If you don't fully know the problem domain, use something with good library support.

I think this is completely fair. As my friend says, "I don't know what library I'll need for every project, but I know where will be one for Python."

Re: Ask HN: Who regrets choosing Elixir?

#89

Earlier quoted context omitted.

However, there definitely is a burden about how much testing you have to write. I generally don't want to have to test every branch of my program to make sure a string doesn't slip through where an int should be or that variables are initialized and not null, etc. This has not been my experience! I've been writing Ruby full-time for about six years (including one of the largest Rails apps in the world) and I don't fi…

My counterpoints would be: First, if you're writing any kind of big code, than somewhere, in your code base, someone else is writing a code where the user name is spelled `username`. Or `user`. You don't have to be "asleep at the wheel" to not remember, or not know, which is which. So you're going to type the wrong one (not necessarily on purpose), and you'll get a runtime error. Not that bad, sure, but you'll get it…

Thank you for the thoughtful reply!

    So you're going to type the wrong one (not 
    necessarily on purpose), and you'll get a runtime 
    error. Not that bad, sure, but you'll get it.
I agree with your facts but not your conclusion here. This certainly happens, but this is trivially caught by your integration tests.

Now, it's certainly true that in a static language, your compiler would catch this for you. In a decent IDE it would be pointed out to you while you type.

However, static or dynamic, you're going to be writing tests anyway, so I can't view this as some sort of increased burden when it comes to writing tests.

    Also, at some point, you'll realize that a string is not the 
    ideal way to represent a user name [1]
I have loved that article since it came out! It's one of those things I saved as a PDF just in case the original goes offline someday.

I don't think your examples are realistic, at all, though.

1. If you replace `User#first_name` and `User#last_name` with `User#name` (which returns a `Name` object) your test suite is going to blow up all over the place every time you call the deleted `User#first_name` or `User#last_name` methods anyway. And now you have a list of places where you need to fix your code.

2. But, what if you update the internal structure of `Name` over time? Some of the above applies. But also callers of `Name` shouldn't know too much about `Name` anyway - it should be providing some kind of `Name#display_name` or whatever function that handles all of the complexity and returns a dang string anyway.

Everything I've written here presumes the existence of a test suite, of course. Which of course takes time to write and maintain. But any nontrivial project needs one anyway regardless of language or type paradigm, right?

    Bottom line: I'll gladly admit that I'm too old and 
    stupid to do that anymore. I had typechecking in 
    the 90s. Give it back.
Absolutely the same here. But, I seem to miss it in different places than you.

I miss static types when I'm writing code. I want my IDE to tell me the types and type signatures when I type, and draw a little squiggly line under my code when I get it wrong. In Ruby, I wind up having 10 different files open at a time in my text editor so I can see what various methods are expecting.

This is mitigated somewhat by simply bashing out a lot of my code in irb/pry directly, since pry's `show-source` can at least tell me stuff.

Re: Ask HN: Who regrets choosing Elixir?

#90

Right tool for the right job, I think. I’ve had absolutely wonderful experiences with Elixir doing web apps (both LoB style and SaaS style). Probably the best one, and it’s such a canonical example, is a group chat app using Websockets. It just feels so good, and with libcluster, multiple nodes in K8s can autodiscover and join each other. No problems at all having chats where the members are connected to websockets o…

let it crash != robustness not required Let it crash is an engineering design for systems large enough that statistically unlikely failures occur regularly. Like a data center or the original use case, telephone switch centers. Even if let it crash worked, it seems philosophically inappropriate for a drone controller.

Yeah; the basic philosophy of 'let it crash' presumes that the issue is bad internal state, and you need to get back into a known good state. If your domain doesn't really have a way to get back to a known good state, or a way to determine a state is good and persist it, it's not going to help you.
Post reply on HN