I really want to try out this language. I love the idea of Erlang but the few times I've had to deal with it (an ejabberd chat server was one) I found it to be a bit too quirky. Every video I've seen with Joe Armstrong leads me to believe he was an under-appreciated genius. Isolated processes and message passing (Actors) seem a good candidate for the future of distributed programming (see this recent video from Crockford on his new Misty programming language [1] - notice how the questions were about Elixir/Erlang). I love pattern matching and elixir uses this to a high degree.
However, I am not 100% sold. Clearly this community punches above its weight, and the BEAM is obviously no slouch and is a brilliant choice for a platform to build upon. But I get concerned looking at the magic of LiveView (and LiveBook). My experience is that if something sounds too good to be true, it often is. A lot of the jargon is also totally unfamiliar to me - things like GenServers and other esoteric kinds of platform specific knowledge. It feels like stepping into a one-way portal, I'm not sure any knowledge in that universe is transferable.
There is a huge risk in niche languages and platforms. Hard to hire for, hard to find libraries for, hard to find blogs/tutorials/etc. With alternatives like Go having a pretty respectable concurrency story ... it is pretty hard to justify taking the risk on such a small community.
But kudos to that community. It is honestly impressive to me what they have achieved. I'm also very excited to see what happens with their type system implementation.
Stuff like LiveView though looks like magic because it is magic. There are a lot of moving parts involved in getting it working. It’s the result of work that has been going on for the past decade across multiple communities though. The ideas are mature even if there is a lot of abstraction.
Stuff like Riak was well ahead of its time. They basically had the idea of being able to create robust distributed systems much the same way you would a GenServer.
However, I just consider some things in the world outside of Elixir/Erlang/BEAM to be a bit more common. For example, using Docker with Kubernetes/ECS/etc as a deployment story. Or connecting micro-services using REST/gRPC/GraphQL. Or using queue systems like SQS or Kafka (to be fair, literally yesterday an ElixirConf talk about using Kafka with Elixir was posted). The advantage of BEAM is that you can avoid that stuff in some cases because it is built in to the platform - but that is a double edged sword since now you aren't doing those things.
Even if I have used Akka for Actors in Scala/Java/Kotlin, something about the BEAM based languages still feels like I am stepping onto the Galapagos Islands and seeing a ecosystem that has been evolving in its own isolation for decades. Sure, there are birds, but they just don't look like any birds I've seen before.
IIRC it was a few years after the dynamo paper came out, and at a time when a bunch of other nosql+high scalability solutions did (e.g. Cassandra)
There is a real need to push Elixir outside its initial circles and it's being done a bit at the moment (I feel).
Both Chris & José are doing a lot of work for exposure outside the initial Elixir circles, and some people in the community are doing the same at their own levels.
Hiring is not that complicated actually, because a fair bit of people want to work in the language (I experience that first-hand), and a good developer will be up-to-speed quite quickly too.
But the "niche" aspect must be tamed in my opinion, and is both a risk (for the language itself) and a short-term reward (being proficient in a niche something is usually quite good).
You also make a very good point about the risk and reward metric. One of the reasons I have been focusing on Elixir as opposed to other niche languages is the progress the community is making is inspiring. I recall an essay by Paul Graham where he talks about Lisp being a contributor to his success with the startup that made him wealthy. He believed that being faster to market with features due to his platform choice was decisive in that success. In that way, something like Elixir (and really, the BEAM) could be a decisive component to some startups success. Given that they are focusing on important contemporary ideas, that is possible. LiveBook for example and Nx/Axon/etc for ML may actually be a killer feature.
Of course, time will tell. I don't have a crystal ball. But I am very interested in watching it all unfold.
It doesn't help you can define your ecto instructions in multiple flavors too (inline vs piping)
You can't mix inline with pipe operators without creating an additiona variable too hold the original query.
If we want to make it less niche, there's definitely good working points there. Once people understand how to hit a database from elixir it magically clicks and the other stuff is more trivial.
On the point of hard to hire and hard to find tutorials. It is true. It is way harder to find for example someone that done what you wanna do in elixir vs javascript. When it comes to libaries it isnt so big problem. There are lots of libraries and so far I havent really encountered any problems. Only thing would be if you interact with an API they might provide a client library for javascript and python (but less common for Go) but not elixir. But honestly it is usually not so hard to use an API with just a http-client.
I am writing Nodejs because I have to, not because I want to. Having used it now, I will never ever start a new project with it.
Nodejs's concurrency controls, ability to scale, tools for reliability and observability are so far behind from the BEAM ecosystem. I sometimes think people think Elixir is too good to be true because they're too used to platforms not designed to be resilient.
What's the magic? I think it's pretty easy to get a correct mental model of everything in livebook maybe excepting exact details of how the diff calculation/data compression works. Even so, you can spy on the websocket messages and get a reasonable picture of what's going on in a pinch.
Liveview is incredibly straightforward.
I don't agree. One of the keynotes from the 2023 ElixirConf was Chris McCord (one of the principle developers of LiveView) describing how it is almost 1.0 [1]. One thing that made me laugh a bit was him emphasizing how much Javascript he's had to write (he was referencing the fact that there is an internal community meme that you can achieve such incredible behaviors without writing Javascript). His quip was "because I wrote it all". I wonder if you asked him "was it straightforward?" what do you think his answer would be?
There are a lot of videos of people talking about how they managed to write something in LiveView. Here is one from 2022 "How I built a block based editor in LiveView" [2]. That is a good one to see how non-trivial tasks can be difficult in LiveView, describing a few times how he had to fight with it to get it to kinda-sorta do what he wanted. Certainly this man will achieve his goal since he is smart and motivated, but his description of his journey does not lead me to a conclusion of "this is incredibly straightforward".
This is impressive tech no doubt, but it is far from clear it is currently reliable enough to stake a startup on. Another tangential issue I have with LiveView is the obvious requirement for a persistent socket connection ... which leads me to wonder how one would approach local-first type developments. Again, certainly possible but definitely not straightforward.
1. https://www.youtube.com/watch?v=FADQAnq0RpA&ab_channel=CodeS...
https://pragprog.com/titles/liveview/programming-phoenix-liv...
People say this every time Elixir comes up, but, I would like to hear evidence of this being true.
I worked at a startup for a couple of years and had never used Elixir. The majority of the engineers who worked there, whilst I did, also hadn't. We had a few who were experts in the language and a couple who had well-known open source packages.
Genuinely from my experience, each of the things in your list are simply not true.
> But I get concerned looking at the magic of LiveView (and LiveBook).
What magic? Again, genuinely questioning this.
In fact it's kind of crazy how much of it I understand given I haven't tried. The community is just all about simple tools and understanding them.
More interesting to me is his physical theories about computation. For example, he hints often at his idea of the blackhole as the most powerful computer. As we think about the distribution of computational systems, I wonder how often we consider the density of computational systems.
I think it is a bit confusing actually, since they differentiate between Erlang (the language), ERTS (Erlang Runtime System) and the BEAM. It isn't clear to me what "Open Source Erlang" refers to with respect to those distinctions, or what the licenses are for each of them.
At my last job I had to do a bunch of HTTP requests in a webhook handler and if enough happened at once, the entire site would just crash due to all the OS processes being busy. I found myself desperately wishing I was just using Elixir instead because it would have been trivial to just spin up a Task to do it and move on.
I get it, of course, it'd be lovely if these complications didn't exist and the same framework handled everything... but I don't think a programming language or even a runtime gets you there. You also need monitoring and ops processes to be part of the solution.
Running heterogenous loads on an elixir cluster is almost trivial. Certainly trivial compared to other languages. No external deps required.
Not all of us can afford elaborate ops teams
The magic of Elixir/Erlang is that all of this logic can live in one codebase and you can choose when deploying your distributed cluster of Elixir/Erlang nodes which nodes run which tasks. The nodes are all aware of each other and when something happens that requires execution of that task it happens on the correct nodes because they're the only ones running those processes. Automagically.
> I want background work to live on different compute capacity than http requests, both because they have very different resources usage
In Elixir, because of the way the BEAM works (the unit of parallelism is much cheaper and consume a low amount of memory), "incoming http requests" and related "workers" are not as expensive (a lot less actually) compared to other stacks (for instance Ruby and Python), where it is quite critical to release "http workers" and not hold the connection (which is what lead to the creation of background job tools like Resque, DelayedJob, Sidekiq, Celery...).
This means that you can actually hold incoming HTTP connections a lot longer without troubles.
A consequence of this is that implementing "reverse proxies", or anything calling third party servers _right in the middle_ of your own HTTP call, is usually perfectly acceptable (something I've done more than a couple of times, the latest one powering the reverse proxy behind https://transport.data.gouv.fr - code available at https://github.com/etalab/transport-site/tree/master/apps/un...).
As a consequence, what would be a bad pattern in Python or Ruby (holding the incoming HTTP connection) is not a problem with Elixir.
> because I want to have state or queues in front of background work so there's a well-defined process for retry, error handling, and back-pressure.
Unless you deal with immediate stuff like reverse proxying or cheap "one off async tasks" (like recording a metric), there also are solutions to have more "stateful" background works in Elixir, too.
A popular background job queue is https://github.com/sorentwo/oban (roughly similar to Sidekiq at al), which uses Postgres.
It handles retries, errors etc.
But it's not the only solution, as you have other tools dedicated to processing, such as Broadway (https://github.com/dashbitco/broadway), which handles back-pressure, fault-tolerance, batching etc natively.
You also have more simple options, such as flow (https://github.com/dashbitco/flow), gen_stage (https://github.com/elixir-lang/gen_stage), Task.async_stream (https://hexdocs.pm/elixir/1.12/Task.html#async_stream/5) etc.
It allows to use the "right tool for the job" quite easily.
It is also interesting to note there is no need to "go evented" if you need to fetch data from multiple HTTP servers: it can happen in the exact same process (even: in a background task attached to your HTTP server), as done here https://transport.data.gouv.fr/explore (if you zoom you will see vehicle moving in realtime, and ~80 data sources are being polled every 10 seconds & broadcasted to the visitors via pubsub & websockets).
My biggest joy comes from how much one can do out of the box with beam/otp. Someone in a team I worked with once said “BEAM/OTP is like k8s only without the complicated parts”.
I spent years learning Elixir but unfortunately the benefits were somewhat undermined by the recent migration to OpenShift (k8s) I had just architected.
It would have been a much more tantalizing proposition if k8s was not in the picture; instead we kept our existing dev stack and utilized k8s concepts to achieve what we would have been doing in OTP, with different trade-offs of course.
I want this but with types. I’m convinced strongly typed is the way to go for larger code bases as it hides the magic of a lot of things and is easier to reason about.
Dialyzer is a static analysis tool for erlang/elixir. It's part of the standard Erlang Release and stands for DIscrepancy AnaLYZer for ERlang programs. It identifies software discrepancies such as type errors, dead code, unnecessary tests, etc., in single Erlang modules or entire (sets of) applications. Dialyzer starts from a base of correct programs and infers types; it doesn't require type annotations but uses them if they are available to provide better warnings.
https://fly.io/phoenix-files/adding-dialyzer-without-the-pai...
https://elixir-lang.org/blog/2023/06/22/type-system-updates-...
So I wish I could have a generic template for a project's GenServer (for basic common registration, memory configuration, logging), that is specialized for a specific task, of which there are three flavours.
Speaking of footguns, Elixir patches this problem with macro code generation, but it is not so good. Any my GenServers are full of trivial repetitions.
And you're absolutely right about genservers being objects :)
I think this is just plain incorrect. The example given later in this paragraph is the Rails `update` method--but the approach used in all canonical Rails examples and generators is the non-exception version of `update`.
I'd argue that now Ruby has some kind of pattern matching, it can take the place of using exceptions for control flow. You can just return the class itself instead of raising it, then match on it.
Do you happen to have an example of such control flow to link to? Not that I don't believe you but I wonder if there's a difference in what is meant by "control flow" between commenters.
begin
do_stuff!
rescue MyFirstLibraryError => error
# handle first
rescue MySecondLibraryError => error
# handle second
end
That could be "control flow" to one person and "error handling" to another. Or even both to a third person.It may be true in Python--I don’t use it much--but I know Ruby well. It was my primary language for 13 years, at multiple companies. I taught it to over a thousand engineers at Airbnb over the course of five years. I still disagree that it is common to use exceptions for control flow.
Maybe it’s a matter of the interpretation of the word “common.” ¯\_(ツ)_/¯
So my question is what are the main selling points of Elixir besides the Erlang environment?
Yes, it is currently dynamic and yes, that means you won't get much int he way of type information from the language itself, it's not currently a statically typed language.
In my experience they include many batteries that would be libraries in other languages, and most times those libraries add a ton of complexity, and sometimes a big learning curve; Async anything is a good example.
I think once one works with Elixir for some time, and gets to learn these builtin tools and abstractions they understand the power of the ecosystem as a whole.
I don't think it compares very well to F# types, but you can use typespecs: https://hexdocs.pm/elixir/1.15.7/typespecs.html
- the native integration of Machine Learning (including compiled to GPU) with e.g. https://www.youtube.com/watch?v=HK38-HIK6NA, Axon, Nx, BumbleBee etc (there is a real push for that in the community)
- the way LiveView works (although there is Blazor in .Net)
- the fact that Elixir can be used in a large number of contexts (including embedded, see Nerves, or scripting but F# is usable for scripting too afaik)
- overall, the BEAM and associated structures
- package manager (Hex) is really nice (but .Net has nice tooling too)
Just a couple of points from my own experience
You can go by with typespecs, pattern matching and guards but that's kind of exhaustive and not really IDE / Intellisense friendly (at least with the community-led Jetbrains plugin).
However, I've been starting to see a lot more Elixir buzz, and I love the looks and sounds of it. Looks like I'll need to start a new side project soon to give it a try.
I don't know anything about Elixir, but it bothers me when people claim that languages like Rust and Go are "not object oriented". If you create structs and have special syntax for defining functions that operate on instances of those structs, then those are called objects and you are doing OOP. Just because some Java people came up with a definition of OOP that says it has to have inheritance, doesn't mean you actually have to pay attention to their definition.
Clearly there are strong preferences among folks for static or dynamic typing, almost like vi vs emacs.
My opinion is on typing is:
Stating typing does not slow you down.
With dynamic typing while you are writing code you have to plan and remember what type a variable of some sort represents before You can act on it.
With static languages most type of variables will be known at compile time and can offer good feedback if a type is used in the wrong manner. It also makes it much easier to know what a variable is and what it might be doing if it has an official type.
Reading dynamic code I often have to search around a bit to figure out what a variable is.
I think if the only reason it is "problematic" to have static typing is because writing "int", "string" etc. is too much work then your priorities are mixed up.
Sometimes people resort to Hungarian notation to help out. (Less and less) sName,iAge,etc. That is helpful but you might as well have static typing at that point.
There has been a lot of work done to find a good way to add (optional) static typing to Elixir and I think it is on going but represents a hard problem.
I am glad Gleam is out (https://gleam.run/), it is my favorite BEAM language now.
> In fact, I might go as far as saying that Elixir gives you a fun language (like Ruby) while leaving out the stateful footguns OOP languages give you.
I don't mean to discount the author's experiences and opinion in this post. It's a nice post with a lot of good food for thought. Likewise I want to share only my own experience here. Two months into any new programming language, it feels like a fun language. Eventually the novelty factor wears off. And then it becomes a boring language. And I say "boring" in a positive sense.
The daily driver languages should be boring. Should present no surprises. Get the job done and get out of my way. When we transistion from the fun phase to that boring phase, what matters more is how good the language is fundamentally designed. As an example in Elixir the pattern matching abilities are great but I don't know if I could justify choosing a language without compile-time type safety in this day and age!
However every Elixir job I've seen requires Elixir experience, so I don't really see much opportunity for working with it professionally.
If you enjoy the Result/Either type and API in Rust, I made this project just for this: https://github.com/linkdd/rustic_result
I also made https://github.com/linkdd/rustic_maybe/tree/main for an Option/Maybe type.
NB: Those are not types, but I'm waiting for set theoretic types to update those libs :)
> I've never written a line of Elixir or Erlang before in my life
Isn't this a red flag to the recruiters and hiring managers?
Maybe it's just me, and I'm not even specifically referring to this particular post ... but why is there so much "debate and discussion" (flamewars?) over static typing recently?
I am guessing this is due to a large influx of people into IT, many of whom got hired because they knew some JavaScript, "React a plus" a lot out of bootcamps and similar programs. As that sizable group ran headlong into static typing, usually via TypeScript, then the debates began.
All over Reddit, Twitter, even HN. People insisting that the verbose and confusing syntax was a "bridge-too-far" and one they would not cross, even if the reasons came down to "it looks weird" or "don't have to 'compile' JavaScript".
Maybe, spending too much heads-down time over the last ... whatever decades at this point ... I simply don't get it.
Don't get it similar to what I quoted here ... "bogged down by a static type system". "Bogged down", in what way?
I spend my days mixed between everything from C# to JavaScript, almost polar opposites. And the rest of it usually in TypeScript.
I do not get this feeling of being "bogged down" by static typing .... ever? I don't recall it being an issue, even though I spent hours in statically typed lanagues yesterday alone. Maybe if you are the author of a complex JS library that has to be shoe-horned into TypeScript ... yes, maybe.
What I did experience recently was being able to remove a parameter from a function, actually compile with the intent it will fail, see it fail, double click on some things and fix the 17 callers to that method I just changed the signature of, and then a minute or so later after fixing those call sites, I was back to work.
Yet, over on the social sites, the debate rages on. Open source projects removing TypeScript practically overnight for "reasons". Questions such as "how many type issues did you run into before switching to TypeScript", leaving me to wonder if that is even a rational question.
And then on the flipside, "TypeScript is the greatest language ever", heart emojis being sent to it, "If you are a JS dev you need to be using TS".
It's all strange to me, having used JavaScript for the first time back in 1996.
I'm convinced it was this wave of JavaScript devs crashing into new requirements to use TypeScript that started it.
It will sort itself out here at some point.
Perhaps it is possible for applications that have small set of requirements.
Well, for existing applications it is extremely difficult, not because Elixir lacks many important features that C# delivers (although I'm sure it does lack some, and vice versa in a big way), but more just because rebuilding any project that's had hundreds or thousands of person-hours poured into it, that's currently satisfying hundreds or thousands of business requirements which may or may not be documented, is a huge project with few rewards; the investment is huge and the return on investment is... I mean, being on {sexy new language} is nice, but nobody's paying you for that.
But that's not what the OP's job is talking about. They just got a different job using Elixir, and are learning the technologies the same way anyone else does, and they're excited about this instance of it enough to write about it.
Having worked with both EF Core and Ecto, I would gladly see the back of EF Core. Ecto is flexible enough to basically do everything you would with a raw SQL statement, but still use a relation mapper with it's validations. And if you have freeform output because you're doing a bunch of JOINs or querying a MATERIALIZED VIEW or whatever, you can just pipe that straight into a struct inline without having to declare a definition somewhere.
It definitely has an initial learning curve, but once you've built a small sample app, it's quite easy to move ridiculous fast.
It still provides access to raw SQL and can always add in Dapper if you want/need more complicated queries. But productivity is really, really good with very little sacrifice in terms of performance.
If you do that then you'll often find loads of other amazing things that are better on the other side.
For example; maybe there's nothing like the MS Entity Framework Core, but instead you'll get to use OTP that everyone's always gushing about which may solve many issues you're having with your current tech stack.
Switching languages (and frameworks and libraries) is amazingly useful because you'll get exposed to solutions you didn't even knew you wanted.
What? Why? Also, what do you mean nothing is as feature complete as Entity Framework? What about ActiveRecord, Ecto, Django ORM, Knex, even Dapper in C# itself......
Many of EF Core replacements do a handful of things well. And after that, it is a cliff.
And now point me to a graphql server that is as feature rich and performant as Hot Chocolate.
Simpler applications will not have complex needs that require feature rich libraries. But, beyond a certain level of complexity, you are on your own. And large orgs do create their own tooling and libraries. But, smaller orgs probably should stick to languages that offer large number of libraries to handle a vast number of problems. Anything else would be putting yourself at a competitive disadvantage.
are we really at the point where rust or elixir in the title = instant karma shower?
I respect your opinion but might I suggest you were not the audience for it? There are maybe three or four posts saying the same thing as you. Nobody has to like or agree with anything that hits front page on HN. And yes, I do agree that HN has a soft spot for Elixir lately, same as AI and LLMs.
It's just that, after many many many months of elixir (pseudo?)brigading it's starting to get tiresome. Thanks for your comprehension.
I found this convenient while writing my first Elixir app with Phoenix Liveview:
"common"? Not IME. Certainly possible, but I don't see it a WHOLE lot. Maybe it's just $CURRENTJOB's style not to, I'll grant.
>I've never written a line of Elixir or Erlang before in my life
How is this possible?
I've been hired to work in Erlang without experience (although I got typecast into php stuff because I did have experience) and although the company was dependent on Erlang, we almost never hired anyone with Erlang experience. Hire smart people that are comfortable learning new things, have a project where the strength of BEAM is compelling, buy a bunch of Erlang books.
My current job is mostly Rust and I didn't know that before I started either. And seems like the team is similar. Smaller team, so I don't know have confidence in my likely to know Rust estimate.
The pool of people who are looking for a new job, and are experienced in Erlang/Elixir, and have exposure to the business specialization, and want to work for a company is very small and hard to find. Hiring companies can ask for it, but they'll have to compromise. It almost certainly doesn't hurt to apply to a job where you don't meet the stated requirements; there's likely no real penalty for having your resume tossed out, and that happens with qualified resumes too. At worst, maybe they blacklist you and won't look at your resume again ever; that's probably ok.
Same thing goes the other way, I'd have preferred an opportunity to use more Erlang, but I didn't find one that met my other needs, so I compromised and write Rust in a cloudy environment and dream about using BEAM and dist and hotloading and all that. Maybe one day.
This is Rails stuff, not Ruby
Are there any more?
None of the companies appear on that list, I think those kinds of things should always be taken with a grain of salt.
you write mix new something. it generates tons of code which will NEVER be update when the template gets update for new people running mix new something.
then all the liveview magic happens with JavaScript code writen by someone who will not be there when major browser security model changes happen (and google make sure these happens every 3mo). then who will keep updating the magic js?
I tried at some point to use Rust for an API but then I depended on making calls to a very complex API. In JavaScript I would have just gotten back a complex object where I can then pick whatever I want progressively through object access methods. In Rust I ended up with more than 500 lines of type definition for the API and it still wasn't enough so I gave up. It is a bit extreme but when you work with an API from an ERP for instance you can get very very complex and extensive types that are not in your control and not very well specified.
Another good example is how complex all ORM internal code get once they try to add static typing. The typing in the ORMs code feels like black magic because they would really really need types as code but don't have it so have to rely on a bunch of crazy generic combinators.
If you don't know what kind of data people give your function and you don't know what's supposed to happen, how can you write that function? I think many people use too strict of a type system. If your function works with any object that has a toString()->string function, then just write an in-line interface that defines it.
I actually love TypeScript here. It allows for `any` if you're prototyping, lazy or simply don't care. It allows mapped types, where the next dev can see how a type came into being - for example, Mutable<T>, Readonly<T>, Required<T>, Partial<T>. The names actually speak for themselves! And it eliminates the Java-style verbosity of creating classes and files all just for a type.
You can always deserialize things as `serde_json::Value` instead of making types for everything to get similar behavior out of Rust.
So then there’s more code for the language devs to write of course, but that’s just the cost of safety, which is highly desirable if you want the technology that you are building to be attractive for use in systems where correctness is critical, which also tend to be highly valuable. As a working professional it’s in my best economic interests to have such valuable tools in my skillset.
Couldn’t that thing be typed as the web format? A nested set of string props with either string or number types in the leaves.
Then use those types to traverse and pull into «real» types?
I like to put structured data into structs in elixir too and the above is essentially what I would do in Elixir.
I don’t know rust well enough to see if I am missing some nuance
In my experience, static typing seemed to lend itself to poor testing, maybe some sort of belief that static types were good enough to not need tests that can prevent regressions. So from my point of view the static typing is negative value. It prevents such a low value class of bugs while seemingly incentivizing people to be lax with the most important classes of bugs.
On the other hand, sometimes I feel like it has a lot to do with test writing. I feel people enjoy static typing, because you can get a feedback loop catching certain things without writing any tests. If you do write tests, all the type errors get caught pretty immediately anyway, so I just don't see the benefit.
Personally, the biggest advantage of dynamic typing for me is the ability to skip formalities for a time. If I want a function that can modify any struct with field "val" in (by, let's say, setting it to zero), I can - and I don't have to do multiple definitions, I don't have to define an interface. Just a function and I am done. If I want to skip error handling for now and only code the happy path, I can - and my program will work (and crash on error, but in some cases it's ok).
As the projects get more complex and require higher safety guarantees, static typing helps in ensuring nothing got broken - but nothing beats dynamic typing for prototyping.
I’m spitballing on the reason - I don’t know why it’s like this. But maybe it’s because static typing encourages you to write & plan your types first. When you know the data types, all the functions kind of flow around your types and everything feels like it obviously flows from that. With dynamic typing, it’s the reverse. I find myself writing my functions first (top down or bottom up) and then backfilling what arguments and data everything needs to pass around. I run my code a lot more as I develop it to stop bugs creeping in. And because I can run my code at any time. There’s no type checker stopping me from trying things out.
Lots of statically typed languages are very strict about their types and have too many of them. You have to admit, when you're trying to build something difficult and focus on getting the business logic part of your program right, the last thing you want to be thinking about is e.g. whether you need a String, ByteString, LazyByteString or any of the other types of strings, and which library decided to accept which one. At some level its definitely useful to distinguish between those, and I'm sure a lot of libraries make sensible choices. But initially in the development of the program its just unnecessary busywork that distracts you from the important bits.
In the past, typed languages also made it a bit harder than necessary to create new types, especially in situations where you have serialization and deserialization. And finally, we had to do all this work, and for what? To be unable to prevent the most basic of errors i.e. NullPointerException? You have to admit, it was a hard sell.
A lot of things have changed, however. TypeScript makes it really easy to define types, anywhere - inline on the function in the argument, or right above it. You can pretend the type is correct at deserialization time, or you can use libraries like `zod` to both define a validator and a type in the same syntax - its up to you. Rust similarly has powerful and easy to use tools like serde - structs and enums are easy to define, and easy to attach metadata to them quickly to (de)serialize them accurately. Huge differences from the old world of getters and setters and NPEs.
When using dynamic languages, there are techniques to make things easier. There is the often mentioned testing, which does help verify a large subset of code paths. Lesser known and more subtle technique is coming up with and following a strict naming convention for properties, for example, and to keep things as consistent as you can in general. This is why you often see so much focus in code reviews about conventions, linting and so on.
Nowadays I guess it mostly depends on how good your dynamic language techniques (at the team level) are, as well as what your treshold for type system annoyances is. There are still some annoyances even in the best type systems, but its gotten way, way better.
I've wondered if that's a thing. It seems to be.
I would definitely use type safety if I had a lot of external data sources. or if there are lots of people working with me. Otherwise, I am beginning to go back to dynamic languages at least for web dev.
Typescript does not solve the fundamental problems of JS. I'm not convinced it really solved the issues related to ingesting data from many different data sources. The data quality issues were still there.
If I were rewriting the whole thing, I'd rewrite it with Elixir (of course), if only to have a sane way of handling errors.
For me, Typescript is more useful, the more of the codebase I "own", because it means I can be more confident that all the types reflect the true types of the runtime data, which means I can change things more confidently and quickly. Do you find that you're refactoring and changing things less with dynamic languages? For me, I think that's the number one magic feature that I miss when I use languages without explicit type systems.
I jumped into some code I haven't touched in half a year the other day so had 0 recollection of any of it, thankfully cause of the types I knew exactly what to pass where and what to expect back without even having to read any of the code other than the type definitions.
I love me some dynamic languages, but damn if it isn't nice to have that kinda power available to you.
The author's joy will also be short-lived because static typing is coming and will likely win out if the implementation is solid.
I'll say types are cool again before I get routed by angry static typing zealots.
These two patterns allow you to write most code, type free, that gracefully handles anything you throw at it while always doing the right thing.
Making changes to such a system is easy and friction free.
Not many type advocates speak of the downsides of type systems, always pitching the net win and ignoring the actual cons.
When you refactor, make a change, or try to add new functionality, and end up fighting the type checker. That's friction to change you are experiencing and that experience is optional.
I get that having discipline in code patterns and the required robustness is a difficult ask at some organizations and some devs. In that circumstance it's better to have a minder in a type system that enforces base the conventions for everyone.
I don't really see that as "fighting the type checker", I see it as the type checker doing its job and warning me of the places where my refactor broke things. The alternative isn't usually that my refactored code magically works, it's that I have to manually grep through the codebase looking for all the places where I might have broken something, and get runtime errors if I don't catch them all.
In that sense the experience of struggling to make a refactor work isn't optional—the choice you get is whether you want the compiler to assist you with it or not.
I realize there are exceptions for certain types of refactors to certain types of functions, but the vast majority of patterns I've ever seen in any codebase—static or dynamic—are patterns that are difficult to refactor without a type checker and trivial to do with one.
To be clear, there are other reasons to prefer dynamic typing, and I'm not trying to say you're wrong to prefer it, but you're not going to get very far trying to persuade a static types fan that refactoring of all things is better in the dynamic world.
I'm not sure exactly what you're saying. If your language is strongly typed, you'll get type errors no matter what. The only difference is whether the type errors happen at compile time or run time. Let's take a hypothetical example:
Let's say I have a programming language called Foo. There are two ways to run programs written in Foo, using the interpreters A and B. A and B are identical, except for that fact that on startup, A will check that the given program is well-typed (static type checking), while B defers such checks to runtime (dynamic type checking).
Given a well-typed program X, I can run X with A or B without ever encountering a type error. Now, I make some changes, like you suggest, and I attempt to run it again. If the resulting code is not well-typed, I will immediately know when trying to run it with A, but with B I have to be lucky enough to hit the specific case that isn't well-typed.
If I understand you correctly, you're saying that you can easily make changes in a dynamic language without _ever_ causing run-time type errors. If that's the case, you would have _exactly_ the same experience whether you ran your code using A or B.
That’s the same argument people always use. “If you account for every case and also have 30 billion unit tests you can avoid all the problems”. The reality is, people don’t. They cut corners, they forget, or they simply make mistakes.
Not only that, debugging a system without types is a terrible experience, and IDEs don’t offer nearly the same level of refactoring support for untyped languages bc it gets very hard or impossible to infer what’s going on.
If it’s a personal project or a small script, untyped languages are fine. Any other scenario you should be using types
the creator of the language has a fantastic talk on this https://youtu.be/giYbq4HmfGA?si=LgSHZupSuR-kMXmj
I can easily see what is passes or returned, and if I'm unsure about the details of the type the answer is a click away, or a short web search away at worst.
Significantly reduces my mental load, allowing me to be vastly more productive.
The cost of this mild inconvenience is still far less than the cost of satisfying types.
This aside, I want typed elixir.
It’s happening: https://elixir-lang.org/blog/2023/06/22/type-system-updates-...
One of the best features of type systems is they make editor completions and navigation work better. Elixir's LS is pretty good, but editor support just isn't nearly as good as what you can have in a good (read: Jetbrains) IDE with a strongly typed lang like C# or Java.
They are working on typing it now from the blog posts I've seen and they will probably be successful because the language is well suited for that in my opinion
Which is orthogonal to what the type is
The shape of the data is much simpler to handle in Elixir than in, to name one, Java.
> a vast number of possible errors caught at compile time
if it's a vast number, usually it's because either:
- you're not familiar with the code base (and you'll make a vast number of other kinds of errors)
- you're making a huge refactory and you're used to rely on a compile-driven workflow, but there are so many alternative ways to handle them
> things go wrong in production
the usual wisdom applies here: static typing does not replace input validation, be it user provided data or function parameters
Having grown accustomed to static typing, not encountering errors until runtime, or worse having errors manifest as type-related misbehavior and potentially not be immediately apparent is much more frustrating than it used to be.
Then you of course have global inference, where you can write code as tersely as in a dynamic language, but still benefit from type checking and add annotations later as a form of documentation for readers.
It also changes the way you code. The best experiences from static typing come IMO from doing type-driven development, where you sketch out your problem first at the type level, then fill out the implementation at the value level. In dynamic languages, you can't program like that. So if you use the same mindset you will find the language limiting.
Weak typing is what causes all sorts of issues.
With dynamic strong types, you can still have a tool like dialyzer figure out potential type issues before shipping, but weak typing means anything goes.
I've seen a lot of authors do crazy type things to get around the type system (like typing out recursion to the n-th level by hand), and I think many open source projects are slowed down by the lack of a type wizard on their team.
But part of doing the correct version is clarifying spec, and prototyping can help with that - so it is a weakly held opinion.
And nothing slows a developer down more than accruing technical debt as they build. It's like having tar stuck to your shoes. You will work the fastest when you have nothing to pay back because your mental model of the application is aligned with the intention of your program.
(That said, I don't think you necessarily need a strong static type system to achieve these aims.)
But once that honeymoon phase is over you usually realize that types and tests were invented for a reason and beyond a certain level of complexity they are absolute must-haves if you want to have high confidence it all works as advertised and to be able to effectively refactor your code.
No matter how much pattern matching you do, and how many typespecs you add to get a better understanding of what's behind a variable, you'll still run into issues at runtime frequently that could have been avoided if it was statically typed.
Dialyzer is great but typespecs and pattern matching only get you so far. You'll always run into situations where the shape of data is not clear, and you have to open a REPL to do an IO.inspect somewhere
I can't imagine building something like this in a dynamically typed language. The way I see it, static typing is like writing inline unit tests to save yourself many, many headaches later.
https://www.crunchbase.com/organization/sumup https://www.sumup.com/careers/positions/berlin-germany/engin...
From my few years off-and-on, Elixir is a nice syntax on top of Erlang. Erlang is intrinsically a flow-based system. Just because you can prove something is turing complete, doesn't make Elixir/Erlang a good general language choice. The no-side effect philosophy is a dead end. You just store state somewhere else anyway, so what have you gained? The current type system is problematic, at best, and everyone knows that. Decades have passed and the same tired phrasing appears in every paper and every talk. "dynamic, scalable language built on Erlang VM, designed for building maintainable, high-concurrency apps". It's clear that this isn't special anymore and it reads as desperate for relevancy, or left to rot. Either way, Elixir/Erlang is a specialty tool like Apache Nifi or CockroachDB. Niche, at best, a sub-optimal choice at worst, for most projects.
It's oft repeated that Elixir/Erlange tooling is "established". Whenever I have used the tooling or a framework (looking at you Phoenix), it's been primitive (Observer has gotten better), poorly implemented (IDEA, VScode), or requires a large amount of memorization to "learn" it. Not the language, the tooling. It's not enough to know Elixir or Erlang. You better understand BEAM (to some extent), shell scripting (just run it in a docker on windows), some glue technologies (something to build the UI, maintain state, etc), and then you can get down to building your backend application.
??? Erlang and Elixir have of tons of side effects. You must not either know the languages well—or maybe you mean immutability?
If you mean immutability, have fun building highly concurrent systems without it. The local reasoning and safety guarantees that immutability gives you makes building large scale applications sane—that’s the only way I’d want to program.
Sorry you are getting downvoted by the hive-mind.
What in tarnation. Please expand on this one.
Pardon?