Honest question, in the era of vibe and AI assisted coding is there any advantages of using untyped programming languages, apart from the fact that non-typed languages has more traning data for the LLM?
This probably controversial, but personally I consider untyped languages as technical debts that need to be fixed sooner or later, and the OP article is partly addressing this very issue.
Rewriting critical software infrastructure (infostructure) to more reliable typed languages happened to most of the Ruby on Rails (RoR) software unicorn stacks for examples Twitter, Airbnb and Shopify to name a few [1],[2],[3].
Oooh, here we go! As a professional Elixir developer for... 10-ish years now, I've been super excited about types coming. I'm very excited that the beginnings have started to land here.
That said, I would love to know how the state of what's in v1.20 compares to un-spec'ed dialyzer. I was under the impression that dyalizer's "success typing" approach (not flagging a function if there are some combination of parameters such that it works, rather than flagging it if some combination of parameters can make it fail) was like what Elixir is doing here, and I haven't found dialyzer terribly useful.
I've seen various posts about Elixir's gradual type system pop up on HN, but haven't been following too closely. Does anyone know whether this particular gradual type system can change the asymptotics of programs vs untyped code? As far as I'm aware, most gradual type systems (e.g. Racket) can make programs run asymptotically slower, although there are some exceptions [1].
Elixir's gradual type system cannot change the asymptotic complexity of your programs. The design explicitly rules out mechanism that causes slowdowns in other gradual type systems (runtime casts at static/dynamic boundaries)
Most gradual type systems insert coercions when values cross the types/untyped boundary (checking every element of a list, wrapping values in typed proxies, etc) but Elixir's team published a "strong arrows" result specifically to achieve soundness without those runtime checks. The bytecode the compiler emits is semantically identical to untyped code.
That can be a concern indeed but it is worth noting that strong arrows compose/propagate. So if you have a function without guards that calls a function that guards on said types, the caller is also strong! We will likely have mechanisms to measure "strength" when we introduce type annotations.
Elixir's heavy reliance on pattern matching has always made it kind of "dynamic language where you still have to think about types" vibe to it. It's also always had a spec meta-language (taken from Erlang) which a lot of people use. You should read up on how they have been implementing the type system, it's pretty interesting! I would not say it's "bolted on." It also has full inference so all codebases get the benefit of it whether you specify types or not.
You didn't like Purescript? It looked pretty cool to me. Its main competition back in the day was Elm, but Typescript has now taken over. From a distance Typescript seems to have too many gaps. I haven't used it though.
I think Elixir is taking a very mature path to typing. No type-annotations (yet) just type inference from existing language constructs like function guards and pattern matching. Also trying to minimize false positives, only giving type errors when it would provably crash at runtime.
I've experienced this, but it's mostly because languages like Python and TypeScript give you way too many escape hatches. I get the intent: allow devs to convert their code base slowly. But in practice it just lets developers opt out of the benefits of typing to "save time" in the short run.
Once you are squarely in a Typescript program and not a "Javascript program gradually adopting Typescript", it would be a good idea to enable Strict mode which forbids implicit-any, effectively meaning the only places you can omit type declarations is where the language will infer the type. Typescript for instance does not infer types of function arguments via their usages (like Flow does), which means in strict mode you must explicitly provide a type for all arguments within a function declaration.
I used to be a bit of a pragmatist when it comes to strict mode, but over the years that has subsided, nowadays I think it is plainly obvious that all Typescript programs should use strict mode unless there's a damn good reason. And I'm not sure there are any legitimate damn good reasons.
True there is no ability to forbid an explicit-any type declaration, though.
I’ve never had a real problem with developers opting out. It’s not that hard to enforce coding standards.
The real problem with Python is the inexpressiveness of its type system and the mess of typed dicts, dataclasses and pydantic classes.
TypeScript may fail narrowing here and there or require a superfluous assert, but usually writing properly typed code, especially with zod, is the path of least resistance.
I keep getting baited by these comments so this is the last one I'll respond to, lol.
Elixir is always been sort of a "typed dynamic language" due to how baked in pattern matching is. Any good Elixir developer has always been thinking about types anyway, it's almost impossible not to.
JS was designed well. Got a lot of things right that others copied later, and also made improvements without breaking compatibility. And the random weird things like [] == 0 don't come up much in actual usage.
I don’t think JavaScript’s syntax was ever designed with the idea that TypeScript would one day exist. Yet somehow it feels like it left the perfect open spaces for TS to later occupy.
They did get lucky with that. The Python type syntax ended up being similar, but the implementation of type-checking is confusing, also it was annoying how you needed to import the types of basic collections for a while.
Im so happy seeing this. We are approaching „great language” level and for me this is the first one.
I would be thankful for pointing at any other language that reliably and safely adds great features and is already convenient to use. I jumped from mastering Go to learning advanced C#, because Go stopped with adding great things :(
I don’t know if it satisfies “already convenient to use”, but IMO ocaml fits “adds great features reliably and safely”. They merged their multicore compiler ~4 years ago, which was a pretty huge change that added parallelism through domains. Notably, they had a working version ~10 years ago, but refused to merge it until they sorted out some performance issues that would have affected existing single-threaded code.
I only say it’s not “already convenient to use” because I heard tons of complaints about the dev environment - mostly that there’s no debugger, no official package manager, etc. But they are working on ‘dune’, and just like the language itself, I got the impression that the dune developers were being conscious to “add great features reliably and safely”. So overall I thought it was a great language/ecosystem, ymmv though.
seems ironic that critics were saying, it needs typing, and all the elixir fans were saying you don't need typing, you don't get bugs related to typing because elixir is somehow magic, now they get typing and it finds bugs for them.... but you said you didn't need that to prevent bugs? But good to see! I spent a bunch of time trying out Elixir a while back, I enjoyed it, but just didn't agree with the lack of types.
It’s not so much language magic as it is “clustering preparedness” IMO.
Since any node in a cluster can be updated at any time and Elixir/Erlang code on the BEAM is designed make it easy to pass function calls to other nodes you don’t have any way of guaranteeing the Type contract between nodes. Types create a sort of false confidence in those situations where pattern matching handles everything very cleanly.
When people say Elixir doesn’t need types it’s not claiming that types are without value. It’s a claim that the mechanisms that already exist are enough without the added complexity.
I appreciate the gradual approach so that we can lean on both.
> you don't get bugs related to typing because elixir is somehow magic,
I've never followed Elixir particularly closely, but what I saw in some Erlang discussions was different. Discourse there was that you need to gracefully handle failure anyhow, so type errors can (should?) just get handled by the failure recovery machinery you're supposed to have anyhow. I disagree with that point of view, but it's much more defensible than "$LANGUAGE is magic".
OP might be referring to Jose Valim's 2023 ElixirConf talk where he's explaining why Elixir should go down the path of types.
He gives a lot more nuanced take than 'types are useless', which is more like 'types are less useful than people think in the context of Elixir development'. (Which makes sense because he's in the middle of implementing a type system for Elixir.)
Yes, that is a great talk. He really does an admirable job of exploring all of the reasons why people think that they want a typed language and concludes many (but not all) are not that helpful.
you succumb to the fallacy that because the compiler let it through, the code wont have any error - the erlang mentality says that the compiler/CPU/everything has errors, how do you handle errors in the general sense
The way to see if it's actually a fallacy, look for in-fighting between the two supposedly opposing camps of goombas.
I've seen internet commenters say China is overstating its economic numbers to look more intimidating, and that China is understating its economic numbers to receive more favourable WTO trading terms, but somehow these two camps never called each other out, which makes me think they're the same people believing that China is both overstating and understating.
How did Elixir manage to attach static type checking to a language after the fact without drastically revising the type system or incurring runtime validation costs? I don't know Elixir, but I have some impression that the BEAM's famous qualities played a role: immutability, "let it crash" philosophy, no inheritance malarkey, etc. Elixir itself had to have a type system that was already relatively orderly for it to be possible to write the relevant proofs way after the fact, right?
Maybe the things that made this transition feasible are the "magic" that used to make people say "Elixir doesn't really need types". Maybe what they meant was something like "Elixir is an orderly language in a bunch of ways that makes the lack of static typing less painful to me than usual".
And I guess we'll see how much people get out of this when they add type annotations later. Maybe the value add will be big after all, and then they'll really be proven wrong. But I can sort of imagine how the apparent contradiction fits together.
It has heavy reliance on pattern matching. In fact, `=` isn't even technically assignment, it's the match operator. Assignment is more of a consequence of matching (though it doesn't have to happen, eg: `1 = 1`). All that to say, most Elixir codebases are written with types in mind, and many are written with pattern matching that would cause a type error at runtime. The new type system just builds off that and moves these errors to compile time (well, not JUST that but ya, this is just meant to be a quick answer).
I can’t swear I’ve never seen that claim - but I can’t remember seeing it if I ever did and certainly it would be a tiny minority position. The actual con arguments are basically “it is nice but has costs, maybe those don’t all get a good return”.
It’s possible that position was correct before set-theoretic type theory was developed.
Elixir predates set-theoretic types. Simon Marlow took a solid crack at typed Erlang 30+ years ago and couldn’t make it work and preserve what Erlang is. 9 years later Success Typings was published and Dialyzer happened. Not the best, but far better than what any other dynamic language had at that time, and Elixir had that available from the beginning.
So it is possible new theory was actually needed to preserve everything that was judged more valuable than types.
It's the circle of life. Dynamically typed language has fans. Other people correctly say that it would be a lot more useful with static types. Fans take this personally and say it doesn't need static types because (they aren't useful anyway/it goes against the spirit of the language/it's only a scripting language anyway/you can just use a debugger/static types hurt productivity/etc. etc.)
Then eventually they add static types. Happened to Python, JavaScript, Ruby... I'm sure there are more.
I think Elixir is interesting and there is real value but some stuff being sold as "all these libs/packages that haven't had any updates for over a year is fine because Elixir" I just don't buy it
and to that point around typing feels like the same wish-washy hand waving from the community that is very off putting
BEAM has genuine use cases but its not as wide as its made to believe. There are very good places where that is a perfect fit but it simply cannot upend Typescript.
Elixir feels very similar to how Clojure started getting traction and then ultimately forgotten apart from its die hard fans, I'm not saying Elixir will go the same way but seems very hard for something new and bold to replace what is popular and boring.
I do want Elixir to succeed (also Clojure as well and I advocated for it for a bit) but the low number of jobs still puts it in similar proximity to Clojure but BEAM I think would still provide uplift where Clojure simply could not
> some stuff being sold as "all these libs/packages that haven't had any updates for over a year is fine because Elixir" I just don't buy it
I maintain more than 20 packages and, except for the major ones, like Phoenix and Ecto, they haven't been updated in more than a year and yes, they are all fine.
The language has been extremely stable. There has been almost no breaking changes in over a decade. Case in point: we introduced a whole gradual type system without making any changes to the language surface! The language is still on v1.x!
So you prefer language communities where libraries have a constant stream of fixes, new breaking change releases every six months and entirely new framework ecosystems ascending every three years?
Not to mention language communities with constant supply chain attacks because its standard library story is poor, and everyone keeps reinventing new, often half-baked solutions?
Or even that, the very same ecosystem congratulates themselves on the typing system but still relies on linters because the language and runtime themselves allow whole categories of dumb ideas to be written?
You can buy it if you use discernment. Obviously you'll run into compatibility issues in certain situations - like you aren't going to be able to use a library coupled to Phoenix 1.3 functionality in a Phoenix 1.8 project, but I continue to be surprised at how I can add a package like https://hex.pm/packages/deep_merge, which is 6 years old and it works just fine.
Phoenix is the exception to the usual rule. It's the only Elixir package where I've encountered substantial friction during upgrades.
Unfortunate, since it's one of the flagship Elixir packages, but I think the upgrades are worth the trouble. Better to improve something than to leave it broken solely for the sake of legacy compatibility IMO.
Im not Jose so I bow to his wisdom but imho thinking about Elixir in types means you arent treating is like a lisp any more, which imho undermines how great Elixir is
in the agent of agents this will probably give us a big boost though so thankyou Elixir team
Oh shit here I go (and learn Elixir for a whole year (again)) again.
I love everything about Elixir, but Elixir constantly makes me doubt myself like no other language. My brain isnt made for functional stuff, but this makes me want to try again.
Sucks that it's not really a beginner friendly ecosystem and usually, when having questions answered, people assume you already know a lot about the language.
don't let the title fool you - the first half of the book is just elixir
over the past 8 years this is the book i've used to ramp back up on elixir and it works like a charm every time - i've never finished it
for me, a mark of a good programming book in this tutorial-project style is that I have started it half a dozen times and never finished it because at some point before the end I've been equipped w/ the tools to go off and do my own thing
Yea I've worked through Elixir in Action and appreciate all book recommendations. My issue is, tutorial style books rarely cover security related concerns.
How to properly build a liveview thats safe against hijacking the websocket phoenix uses for liveviews. You can just do it from the devtools on client side. With regular HTTP requests at least I know what to look out for, with liveview there are almost no resources on how to build a view securely. Like I was able to just call the functions in my module by just addressing them from my browsers console. Just to name an example.
There's a guide in the LiveView docs that walks you through the security model. To be clear, you need to always assume that the user can send you anything. That's a fact of any networked system: Clients need to be assumed to be completely under the control of an evil user, because at the end of the day it is impossible to know whether you're talking to the client you wrote, or some evil program written by an adversary. Any function that acts as a handler for an event/message can be called by the user, at any time. You have to use session/socket state to handle authorization.
Honestly just build it using the tutorials and sound mind and you're like 80% there.
This may sound crazy but when any interpreter boots up, but I feel it especially with BEAM, that needs to be your "let there be Light" moment. That's your world, that state is yours and only your will decides what changes.
So yes you can call all functions in your module, that's indeed how it works. But that's your module and that function mutates your world.
Just like you filter what people tell you based on your knowledge, you do the same here.
Most of my methods start with guard clauses.
`return if condition_not_met`
Don't touch my state if I don't agree with what you want me to do.
In Ruby it's essential cause that's how we get RuntimeErrors all over the place. In Elixir it's way easier to do, with pattern matching. And easier since state is what enters the function and will be what leaves.
If you keep this in mind you should inherently write safe code, because in protecting your domain through guards you basically close the door for exploitation by unknown means.
I'll give you one example I just thought of. Where I work we run Rails since the time before time, and as such had a lot of technical debt.
Around Rails 5 or 6 what we call `ActionController::Parameters` had a breaking change. Basically this module processes parameters received from HTTP requests.
Beforehand it just wrapped all it got and handed it over to us. But now it expected us to tell it what to expect. And if didn't find what it expected it blew up with a bang!
Horrible for our hundreds of controllers with `controllers * 4` html templates where all the form keys were hidden.
We either had to add the conventiely available `permit!` call, or find the form keys for all the forms, and add `permit(:name, :address,...)`. A shitload of work before AI.
I ended up monkey patching Rails to generate the lists for us instead of crashing. And for the point of this entire story...
The defaults of most frameworks are very safe, but they require the most verbosity so the framework knows what to expect and to guard it. But there always exists easier and faster ways to the same goal, but it's generally a trade. You get ease, you sacrifice some security.
Don't get in that habit and you'll be fine. And spend a lot of time thinking what could go wrong and guard against them.
As someone who learned Elixir during the Phoenix 1.7 release, let me tell you: If you downgrade to Phoenix 1.6 and learn from there, you should be fine.
The upgraded versions are mostly the same, but the differences in Phoenix 1.7 are enough to break the tutorials enough to confuse a newbie. Now, in the post-LLM age, that's not nearly as bad. But it was a real pain when I was learning.
> I love everything about Elixir, but Elixir constantly makes me doubt myself like no other language. My brain isnt made for functional stuff, but this makes me want to try again.
I experienced this really painfully when I was in college and took a kind of "survey of programming paradigms" course and tried Haskell for the first time. I'd been programming for years by then, and I couldn't believe how helpless I was at trying to complete things that had long felt "basic" to me.
But I don't think it's about the brain not being suited, I think it's that contrast of your experience level in imperative languages vs. the fact that when working in a pure functional style, you start out as a newbie again.
I think you'll gradually improve. I think the thing that finally made functional programming feel comfy for me was realizing how much I love composing code that basically feels like more generously spaced Bash "one-liners". The data starts out in one shape, so you run a command to dump it. Then you think of a step that gets it closer to what you want, you pipe it to that next command, and you take another look. And you keep going and at the end what you're looking at is typically pretty close to a series of transformations of data that you never mutate!
Part of what makes this feel comfy in the shell is that you build up that vocabulary of commands just by puttering around your file system every day. Over the years my library of familiar "functions" in a Unix-like environment has grown quite large. In a pure functional programming environment, you have to do the same thing but it takes a little more effort to learn the vocabulary. Your most frequently used "commands" will be functions like map, fold, and zip instead of grep, cat, or sort. But the core of it is really the same, and what I love about building pipelines applies equally to both: you can build it piece by piece, and for each puzzle you're on, you can forget about the previous steps and just think about the next transformation of the data that's in front of you. There is something refreshingly, relaxingly low-context about that.
Anyway I hope you give it a try and enjoy it. When we can learn to enjoy being bad at something, that's how we finally get good at it.
Come hang out on Elixir Forum! Lots of friendly folks there who are happy to answer (and re-answer) beginner questions. It's not quite what it was a few years ago thanks to LLMs, but it's still quite active.
EDIT: I see my cohort has already given you this suggestion :P
Yea I've posted there twice as far as I remember. You will absolutely get help, whether you understand the answers is a whole different story.
Elixirs community is great. Its just hard to learn because it's not yet widely adopted, there are no (non senior) roles for it and it's a lot of work understanding all the BEAM concepts. A thing just being interesting isn't enough motivation for me to learn, I need a bigger goal but with Elixir there do not seem to be any.
My last experience with it was building something with Phoenix Liveview until I noticed how easily you can hijack the websocket and just spam random commands to your server or temper with payloads (with regular webapps ive built i never had this issue). Which made me quit that project.
Fair. If you have this friction then it's not worth pursuing.
One thing that really helped me pick it up was saying YOLO and rewriting one part of the business stack from Ruby on Rails to Elixir. It taught me quickly and well.
The official guides are also great and IMO you can get through them all without a rush in two weekends. But again, if you don't want to then don't.
You can also try asking right here in this HN thread. Maybe I or others would be willing to give you a more detailed response.
When building I couldn't get "what if I have ghost processes", "what if I spawn too many processes", "what if this architecture is bad compared to...", "when to kill processes", "whats the correct restart strategy for this" out of my head... It's so confusing to build for the BEAM that I ultimately gave up on it.
> It's so confusing to build for the BEAM that I ultimately gave up on it.
Every new paradigm is confusing if you don't put in the work to learn it. That's just how the mind works.
What's important is what you get after you don't give up on it long enough. And that, on BEAM, is a hilariously OP superpower of effortlessly[1] parallelizing and distributing workflows. Then there are Elixir macros and the OTP supervision model. The addition of gradual typing is huge, and when the annotation syntax lands, I will definitely switch to Elixir for everything on the backend.
In any case, the only thing I can tell you is that learning Elixir is worth enduring the confusion. From personal experience, it's just a matter of learning it bit by bit over time - there's a finite set of "confusing" ideas in the OTP/Elixir/BEAM mix, and learning about some of them every other day works wonders over a few months.
[1] An exaggeration - I know! But it does make it much easier to implement parallel and distributed workflows. Recently, most of the important languages finally started getting their m-n concurrency models (from Java to Python), so the BEAM is not as much ahead on SMP, but for distribution (you can send closures to execute on different machines transparently!) it is still in a league of its own.
I'm not sure what a ghost process is? I guess something that's living beyond its usefulness / isn't supervised, etc? ... I don't speak Elixir, but you can do the equivalent of this Erlang to see everything on the node:
rp([{X, erlang:process_info(X)} || X <- erlang:processes()]).
Then you'll know what's going on. Caveat: if you have a lot of processes, that's going to use a bunch of memory; for production you probably don't want to use erlang:process_info/2 with specific items instead of the default items. And you might don't want to output something for all the processes if you have a lot of "normal" processes that won't need to be listed.
> "what if I spawn too many processes",
The default limit is 1,048,576, if you want to have more, you can add +P X to the erl command line with a bigger limit? Have your monitoring alert you when you're at ~ 80% of the limit.
> "what if this architecture is bad compared to...",
This probably addresses the real question of your too many process question. If your architecture is bad or if you spawn more processes than a good architecture would, your performance will be bad. If your architecture is really bad, you'll have a hard time solving the problems you're trying to solve. Future you will look upon your system and despair; you may also despair in the present...
Eh, you're going to make bad architecture. BEAM won't solve all your problems. But, if you've got problems it can solve, IMHO, it can be a very nice way to solve them.
> "when to kill processes",
Kill processes (or let them crash) when they misbehave. Kill them (or let them exit normally) when they've done their work and they don't have anything else to do or wait for. When you spawn a process, you'll often have a pretty good idea of the conditions that would lead to its death... Ex: if you spawn a process to handle a connection, it should probably die around the time that the connection ends. If you spawn a process to handle a request, it should probably die when the request is handled. If you spawn a process to listen for connections, it probably should die when you don't want to listen anymore. Etc.
> "whats the correct restart strategy for this"
Well... it depends. Almost never the default strategy. The default strategy is a big foot gun; at least it is for Erlang, maybe they changed it in Elixir. I need zero hands to count the number of times I actually wanted BEAM to stop because some supervised process failed 3 times in a small time frame; but it's happened to me a lot more times than that. For per connection or per request things, the appropriate strategy is not to restart at all; for other things, try to restart a few times quickly then maybe every minute or so is probably sufficient. You'll want some sort of alerting. And if the restart strategy isn't right, you can always console in and poke it.
I haven't dug into this for a while, bit you should be able to define a catch-all event to return a respond to non-compliant requests . It should be built-in to some degree IMO, but I think it's not an unsolved problem.
Do you maybe know some Rust? I'm also not that experienced with FP languages, but Gleam felt familiar enough, due to some Rust-isms, to allow me to focus more on the concepts rather than the syntax. Granted, I spent a few afternoons with it, but if I were to pick a FP language again to wrestle my brain into submission, I'd probably go with Gleam due to familiarity.
I gave up on Rust even quicker than on Elixir haha.
But yea I know about Gleam and I did build some fourier transform stuff with Rust a while back. I like Gleam generally. I am just much much slower with FP and think its extremely unintuituve compared to, say, Go for example.
The confusing state riddling here happens in the background as your whole app basically is a state. The thing that really throws me off with Elixir is having to handle (possibly) hundreds of thousands of processes. Doing this correctly seemed impossible to learn for me.
> Elixir is having to handle (possibly) hundreds of thousands of processes
OMG, why? Why would you ever have so many processes? All of them at the same time? Are you going to animate a 3D scene and run a process for each vertex, or something?
No, I mean, if you're WhatsApp - across all nodes - then somehow maybe yes? At scale. But in normal code, slicing workloads too thinly is counterproductive, and having even tens of thousands of processes is a sign that you're slicing it way too thin. Message passing between processes is cheap, but not free. Schedulers do a good job, but rarely have more than 16 cores to work with. And so on.
You can have that many processes if you want, to be sure. But if you're struggling with it, why would you want it?
Reading your comments in this thread, I have a feeling you just didn't spend enough time reflecting on how you want to use Elixir. In effect, you also failed to consider how exactly you should learn it. For example: Elixir is a perfectly capable procedural language. Start by writing CLI tools, without spawning any processes at all. Then try to parallelize their processing. If the tool accepts a list of files as arguments, use a `Task` to compute return values for each file. Tasks are processes, but with a particular contract that simplifies their usage. Later, you can experiment with error handling and supervision by putting the tasks under a supervisor. And so on. You go from the familiar to the less familiar, with a useful, working tool every step of the way.
It's not like you're dealing with hundreds of thousands of ad-hoc processes. If you're writing a web server, for example, each of these processes might simply be a client connection and they all operate the same. The fact that there are 2 or 100,000 is only a problem for the BEAM scheduler.
Sounds like there is some foundational knowledge of Elixir that you miss and everything seems more confusing than it should be. To me writing a 'server' in Elixir is orders of magnitude easier than doing it in Python, Rust or C++.
As someone else suggested, bring your concerns to the Elixir Forum and surely someone will clarify them for you
I'm working on a game engine right now (written in object oriented language, of course) and I keep itching to design a compiled functional language for games, because state spread in thousand of objects, eldritch class hierarchies, are complete hell.
Once you taste Elixir/Erlang, there is no going back to the madness.
Do https://htdp.org and follow all the exercises carefully (yes, it will feel like baby work at first) - you will retrain your brain for functional stuff. :-)
With Elixir specifically it was the learning experience I had with Phoenix. I didn't understand how a Phoenix app booted, didn't know where to edit my config.
Syntax like:
I love Elixir and Phoenix, but Phoenix especially uses a lot of compile-time macros and it can be a steep learning curve when you need to pull apart the skeleton framework to figure out how things are actually wired.
I pretty frequently find myself needing to open up the source to understand what's actually going on, the docs aren't bad but it often feels like they assume a lot of existing familiarity with phoenix.
In this example, `socket` is a compile time macro and it's being called with
and what is does is register that data with the `phoenix_sockets` attribute inside the module you called `socket` from. At compile time that gets turned into a lookup inside your module, and presumable then the UserSocket module is invoked when a websocket request hits the specified path.
Would you find it more clear if socket was called like this?
I find the optional parentheses, and the way that keyword lists are defined to be the two biggest stumbling blocks when I come back to Elixir after a while way.
example "with", 3, extra: "arguments", as: "a", keyword: "list"
always takes some extra mental effort to get through, especially when there's no parenthesis. But I appreciate not having to write all the extra brackets and parens when I get going, so I think it's a fair tradeoff.
Socket is a behavior, which is like a trait or interface. MyAppWeb.UserSocket implements the behavior. It's basically a convenience over having to write a bunch of repetitive WS or long poll handling every time you want a socket like thing. Its pretty well documented https://phoenix.hexdocs.pm/Phoenix.Socket.html.
Your last sentence is basically where I'm at, writing my backends in Rust these days. I'm interested in the BEAM promise of letting things crash but not sure how good that is in Gleam due to its OTP still being somewhat immature as the devs are rewriting GenServer as a typed library.
Hello! I'm the maintainer of Gleam. We are not rewriting OTP, regular OTP is used in Gleam. Most commonly the typed Gleam APIs for OTP are used, but you can use the untyped Erlang APIs if you wish.
This is the same as in Elixir, where macro-enabled APIs are offered, and they just wrap the regular Erlang APIs.
I wrote both Elixir and Erlang code. Erlang is just useless to me as a
programming language; it has many great ideas though. I love the idea
of being able to think in terms of immortal, re-usable, safe objects
(Erlang does not call these objects, but to me this is OOP by Alan
Kay's definition. I don't use e. g. the java definition for OOP.)
Elixir built on that and made Erlang code optional, meaning people
could write more pleasent code. And here it succeeded. I am not sure
why Elixir succumbed to type madness now, but the comment that
"writing Elixir is like writing Erlang", is just simply not true.
Elixir is significantly better than Erlang with regard to writing
code. José Valim got inspiration for Elixir from ruby, to some
extent.
You're taking my comment way too literally. I'm basically just making a syntax comparison. Obviously Rust is not at all like Gleam in many ways either. It's just statically typed and has a similar syntax.
I agree that actor languages are the purest form of OOP as Alan Kay has expressed it. And unlike Smalltalk, Erlang just accepts that some things are naturally functions, not messages.
Smalltalk has no problem at all with accepting that some things are naturally functions: it has always had blocks! The call operator is `value`, not `()`, but it's the same "apply a piece of code to some values" operation.
Gleam doesn't have macros, which many Elixir libraries (such as Phoenix and Ecto) uses to great effect.
Gleam for example has issues with verbosity of decoding/encoding json whereas in Rust you derive serde and in Elixir it's just a function call away.
Elixir has a more mature ecosystem. While you can for example use Phoenix with Gleam (or some other Gleam framework) the experience just isn't the same.
The big draw with Gleam over Elixir is the typing (where Elixir is now closing the gap) and being able to compile to JavaScript (which is also what Hologram is doing for Elixir).
I prefer Gleam's typing system and the Rust-like syntax, but for now I feel Elixir is the better choice for all my web dev projects.
Apparently it is not that difficult to add different compiler backends. There was a presentation [0] recently about adding wasm support as a compiler target. The implementation was quite far along, including support for the wasm component model.
I have the great luck to work in many different stacks as a freelancer.
One of them is Elixir. While I am on this project for just half a year and not too many hours per week, I can say: I absolutely love this language.
It reminds me of Haskell, which I had courses on at university, and is just an absolute joy to work with.
My only gripe was that there was no typesystem. So I was eyeing Gleam (as I also like Rust very much), but as Gleam doesn't and probably never will support Ecto and Phoenix (due to it not supporting macros), it's a nogo for the project at hand.
I knew Elixir was to gain a typesystem, still this is absolutely fantastic news.
Super stocked to work with this.
I think this also shows that merely copy/pasting
ruby's syntax, isn't an automatic win. I noticed
this before with crystal, though naturally crystal
had types from the get go.
Fundamentally:
def foo()
end
should stay simple. And this is no longer the case now.
(Ruby also went in error, e. g. "endless methods". I don't
understand why programming languages tend to go over the
edge in the last 5 years or so.)
The syntax you are commenting on has always existed in Elixir, before v1.0, as part of patterns and guards.
You are commenting as if we added this now but we have made no changes to the language surface. The difference is that we now leverage these same language constructs to extract precise type information.
You need Beam and the Elixir. I find that really weird, because I'm used to just the language like in Python, Java, C, Rust. Not something underneath it, too.
There is no debugger. The way to debug Elixir is to print stuff to the console, like 40 years ago. No thanks.
> You need Beam and the Elixir. I find that really weird, because I'm used to just the language like in Python, Java, C, Rust. Not something underneath it, too
The beam is a VM. You get that Java requires a VM too right? It’s called JVM for a reason. And Python requires an interpreter.
> There is no debugger. The way to debug Elixir is to print stuff to the console, like 40 years ago.
That is false. https://www.erlang.org/doc/apps/debugger/debugger_chapter.ht... and you have observer. And you have a lot of other debugging tools. I hear Java has a good one and maybe it’s better (I never used it) but it’s not true there exist no debuggers for the beam.
I meant that it doesn't get much love from the community, it's pretty clear it's not used much, that's why things like `dbg` gets added to the language.
If you're going to try and use this analogy, you need to compare Elixir to Kotlin or Scala or Clojure rather than Java. Elixir is a language written for the BEAM which was created for Erlang. The BEAM happened to be useful VM for these other languages such as Elixir, Gleam, LFE, & Luerl.
If you don't want to then fair enough :) that said if your problem is just installation, some of the gleam people realized it can be tricky and made a nice guide for various operating systems and package managers: https://gleam.run/install/
Note this includes installing erlang as well
While it is multiple steps, the frustration is a much more one time thing compared to the problems and frustrations you'd have using a language or its ecosystem for a long time or big project
For Java you need a JRE and JDK depending on whether you're just running or also building. That they are bundled (for Windows) is slightly convenient, but they're not bundled on Linux so what you're saying is OS dependent
Nah, I work on a team that has multiple microservices written over the years in different versions of Java. "Just click the installer" is not sufficient. That's why programs like jenv, SDKman, nvm, and others even exist (and are popular). Your lack of real-world experience is showing.
If you're used to Java, Elixir is like `javac`, Beam is like `java`. Mix is like a (way better) version of Gradle. You need elixir to compile your app, you only need the Beam to run it. Once you've built your project, you don't need Elixir anymore exactly like java/javac. C and rust compile to machine code so don't have a runtime dep, but otherwise they still require you to have a compiler at build time, just like elixir.
To be fair, there is more than just print debugging. You have access to tools like red(x)bug https://github.com/nietaki/rexbug, the Elixir-LS project has Debug Adapter Protocol support. And in my opinion, the REPL (and decent software architecture) makes it easy to investigate your code by just running the functions as needed (even if your live production system if you want).
This probably controversial, but personally I consider untyped languages as technical debts that need to be fixed sooner or later, and the OP article is partly addressing this very issue.
Rewriting critical software infrastructure (infostructure) to more reliable typed languages happened to most of the Ruby on Rails (RoR) software unicorn stacks for examples Twitter, Airbnb and Shopify to name a few [1],[2],[3].
[1] Why did Twitter move away from Ruby on Rails?
https://www.quora.com/Why-did-Twitter-move-away-from-Ruby-on...
[2] How Airbnb Scaled by Moving Away From a Rails Monolith:
https://www.reddit.com/r/programming/comments/1756q7z/how_ai...
[3] Is Shopify shifting away from Rails?
https://news.ycombinator.com/item?id=33409597
That said, I would love to know how the state of what's in v1.20 compares to un-spec'ed dialyzer. I was under the impression that dyalizer's "success typing" approach (not flagging a function if there are some combination of parameters such that it works, rather than flagging it if some combination of parameters can make it fail) was like what Elixir is doing here, and I haven't found dialyzer terribly useful.
[1] https://doi.org/10.1145/3314221.3314627
Most gradual type systems insert coercions when values cross the types/untyped boundary (checking every element of a list, wrapping values in typed proxies, etc) but Elixir's team published a "strong arrows" result specifically to achieve soundness without those runtime checks. The bytecode the compiler emits is semantically identical to untyped code.
that said, I'm a fan
[0] https://github.com/lustre-labs/lustre
I used to be a bit of a pragmatist when it comes to strict mode, but over the years that has subsided, nowadays I think it is plainly obvious that all Typescript programs should use strict mode unless there's a damn good reason. And I'm not sure there are any legitimate damn good reasons.
True there is no ability to forbid an explicit-any type declaration, though.
The real problem with Python is the inexpressiveness of its type system and the mess of typed dicts, dataclasses and pydantic classes.
TypeScript may fail narrowing here and there or require a superfluous assert, but usually writing properly typed code, especially with zod, is the path of least resistance.
Elixir is always been sort of a "typed dynamic language" due to how baked in pattern matching is. Any good Elixir developer has always been thinking about types anyway, it's almost impossible not to.
I don’t think JavaScript’s syntax was ever designed with the idea that TypeScript would one day exist. Yet somehow it feels like it left the perfect open spaces for TS to later occupy.
I think that's part of the reason that LLMs do so well with it, despite its relative lack of popularity.
I would be thankful for pointing at any other language that reliably and safely adds great features and is already convenient to use. I jumped from mastering Go to learning advanced C#, because Go stopped with adding great things :(
I only say it’s not “already convenient to use” because I heard tons of complaints about the dev environment - mostly that there’s no debugger, no official package manager, etc. But they are working on ‘dune’, and just like the language itself, I got the impression that the dune developers were being conscious to “add great features reliably and safely”. So overall I thought it was a great language/ecosystem, ymmv though.
Since any node in a cluster can be updated at any time and Elixir/Erlang code on the BEAM is designed make it easy to pass function calls to other nodes you don’t have any way of guaranteeing the Type contract between nodes. Types create a sort of false confidence in those situations where pattern matching handles everything very cleanly.
When people say Elixir doesn’t need types it’s not claiming that types are without value. It’s a claim that the mechanisms that already exist are enough without the added complexity.
I appreciate the gradual approach so that we can lean on both.
I've never followed Elixir particularly closely, but what I saw in some Erlang discussions was different. Discourse there was that you need to gracefully handle failure anyhow, so type errors can (should?) just get handled by the failure recovery machinery you're supposed to have anyhow. I disagree with that point of view, but it's much more defensible than "$LANGUAGE is magic".
He gives a lot more nuanced take than 'types are useless', which is more like 'types are less useful than people think in the context of Elixir development'. (Which makes sense because he's in the middle of implementing a type system for Elixir.)
https://youtu.be/giYbq4HmfGA?t=571
https://en.wiktionary.org/wiki/Goomba_fallacy
I've seen internet commenters say China is overstating its economic numbers to look more intimidating, and that China is understating its economic numbers to receive more favourable WTO trading terms, but somehow these two camps never called each other out, which makes me think they're the same people believing that China is both overstating and understating.
Maybe the things that made this transition feasible are the "magic" that used to make people say "Elixir doesn't really need types". Maybe what they meant was something like "Elixir is an orderly language in a bunch of ways that makes the lack of static typing less painful to me than usual".
And I guess we'll see how much people get out of this when they add type annotations later. Maybe the value add will be big after all, and then they'll really be proven wrong. But I can sort of imagine how the apparent contradiction fits together.
It’s possible that position was correct before set-theoretic type theory was developed.
BASIC, Smalltalk vs Strongtalk, Common Lisp, Dylan
It is the eternal September.
So it is possible new theory was actually needed to preserve everything that was judged more valuable than types.
Then eventually they add static types. Happened to Python, JavaScript, Ruby... I'm sure there are more.
Statically typed languages put the onus on the caller to transform the data into the shape(s) required.
Dynamically typed languages put the onus on the called to handle anything.
That is, in a dynamically typed environment your function has to defensively code for every possible type it could be handed.
and to that point around typing feels like the same wish-washy hand waving from the community that is very off putting
BEAM has genuine use cases but its not as wide as its made to believe. There are very good places where that is a perfect fit but it simply cannot upend Typescript.
Elixir feels very similar to how Clojure started getting traction and then ultimately forgotten apart from its die hard fans, I'm not saying Elixir will go the same way but seems very hard for something new and bold to replace what is popular and boring.
I do want Elixir to succeed (also Clojure as well and I advocated for it for a bit) but the low number of jobs still puts it in similar proximity to Clojure but BEAM I think would still provide uplift where Clojure simply could not
I maintain more than 20 packages and, except for the major ones, like Phoenix and Ecto, they haven't been updated in more than a year and yes, they are all fine.
The language has been extremely stable. There has been almost no breaking changes in over a decade. Case in point: we introduced a whole gradual type system without making any changes to the language surface! The language is still on v1.x!
Or even that, the very same ecosystem congratulates themselves on the typing system but still relies on linters because the language and runtime themselves allow whole categories of dumb ideas to be written?
Unfortunate, since it's one of the flagship Elixir packages, but I think the upgrades are worth the trouble. Better to improve something than to leave it broken solely for the sake of legacy compatibility IMO.
Really? All the Elixir fans were saying that?
in the agent of agents this will probably give us a big boost though so thankyou Elixir team
It is really excellent!
I also wonder if this works well with Ruby’s duck-typing and monkeypatching.
I love everything about Elixir, but Elixir constantly makes me doubt myself like no other language. My brain isnt made for functional stuff, but this makes me want to try again.
Sucks that it's not really a beginner friendly ecosystem and usually, when having questions answered, people assume you already know a lot about the language.
don't let the title fool you - the first half of the book is just elixir
over the past 8 years this is the book i've used to ramp back up on elixir and it works like a charm every time - i've never finished it
for me, a mark of a good programming book in this tutorial-project style is that I have started it half a dozen times and never finished it because at some point before the end I've been equipped w/ the tools to go off and do my own thing
There's a guide in the LiveView docs that walks you through the security model. To be clear, you need to always assume that the user can send you anything. That's a fact of any networked system: Clients need to be assumed to be completely under the control of an evil user, because at the end of the day it is impossible to know whether you're talking to the client you wrote, or some evil program written by an adversary. Any function that acts as a handler for an event/message can be called by the user, at any time. You have to use session/socket state to handle authorization.
This may sound crazy but when any interpreter boots up, but I feel it especially with BEAM, that needs to be your "let there be Light" moment. That's your world, that state is yours and only your will decides what changes.
So yes you can call all functions in your module, that's indeed how it works. But that's your module and that function mutates your world.
Just like you filter what people tell you based on your knowledge, you do the same here.
Most of my methods start with guard clauses.
`return if condition_not_met`
Don't touch my state if I don't agree with what you want me to do.
In Ruby it's essential cause that's how we get RuntimeErrors all over the place. In Elixir it's way easier to do, with pattern matching. And easier since state is what enters the function and will be what leaves.
If you keep this in mind you should inherently write safe code, because in protecting your domain through guards you basically close the door for exploitation by unknown means.
I'll give you one example I just thought of. Where I work we run Rails since the time before time, and as such had a lot of technical debt.
Around Rails 5 or 6 what we call `ActionController::Parameters` had a breaking change. Basically this module processes parameters received from HTTP requests.
Beforehand it just wrapped all it got and handed it over to us. But now it expected us to tell it what to expect. And if didn't find what it expected it blew up with a bang!
Horrible for our hundreds of controllers with `controllers * 4` html templates where all the form keys were hidden.
We either had to add the conventiely available `permit!` call, or find the form keys for all the forms, and add `permit(:name, :address,...)`. A shitload of work before AI.
I ended up monkey patching Rails to generate the lists for us instead of crashing. And for the point of this entire story...
The defaults of most frameworks are very safe, but they require the most verbosity so the framework knows what to expect and to guard it. But there always exists easier and faster ways to the same goal, but it's generally a trade. You get ease, you sacrifice some security.
Don't get in that habit and you'll be fine. And spend a lot of time thinking what could go wrong and guard against them.
The upgraded versions are mostly the same, but the differences in Phoenix 1.7 are enough to break the tutorials enough to confuse a newbie. Now, in the post-LLM age, that's not nearly as bad. But it was a real pain when I was learning.
I experienced this really painfully when I was in college and took a kind of "survey of programming paradigms" course and tried Haskell for the first time. I'd been programming for years by then, and I couldn't believe how helpless I was at trying to complete things that had long felt "basic" to me.
But I don't think it's about the brain not being suited, I think it's that contrast of your experience level in imperative languages vs. the fact that when working in a pure functional style, you start out as a newbie again.
I think you'll gradually improve. I think the thing that finally made functional programming feel comfy for me was realizing how much I love composing code that basically feels like more generously spaced Bash "one-liners". The data starts out in one shape, so you run a command to dump it. Then you think of a step that gets it closer to what you want, you pipe it to that next command, and you take another look. And you keep going and at the end what you're looking at is typically pretty close to a series of transformations of data that you never mutate!
Part of what makes this feel comfy in the shell is that you build up that vocabulary of commands just by puttering around your file system every day. Over the years my library of familiar "functions" in a Unix-like environment has grown quite large. In a pure functional programming environment, you have to do the same thing but it takes a little more effort to learn the vocabulary. Your most frequently used "commands" will be functions like map, fold, and zip instead of grep, cat, or sort. But the core of it is really the same, and what I love about building pipelines applies equally to both: you can build it piece by piece, and for each puzzle you're on, you can forget about the previous steps and just think about the next transformation of the data that's in front of you. There is something refreshingly, relaxingly low-context about that.
Anyway I hope you give it a try and enjoy it. When we can learn to enjoy being bad at something, that's how we finally get good at it.
EDIT: I see my cohort has already given you this suggestion :P
Sometimes posts don't get traction due to ambiguity, and some smelled like "do my homework" so people ignored them.
But every post with a genuine curiosity in it gets answered, as far as I can tell.
Elixirs community is great. Its just hard to learn because it's not yet widely adopted, there are no (non senior) roles for it and it's a lot of work understanding all the BEAM concepts. A thing just being interesting isn't enough motivation for me to learn, I need a bigger goal but with Elixir there do not seem to be any.
My last experience with it was building something with Phoenix Liveview until I noticed how easily you can hijack the websocket and just spam random commands to your server or temper with payloads (with regular webapps ive built i never had this issue). Which made me quit that project.
One thing that really helped me pick it up was saying YOLO and rewriting one part of the business stack from Ruby on Rails to Elixir. It taught me quickly and well.
The official guides are also great and IMO you can get through them all without a rush in two weekends. But again, if you don't want to then don't.
You can also try asking right here in this HN thread. Maybe I or others would be willing to give you a more detailed response.
Every new paradigm is confusing if you don't put in the work to learn it. That's just how the mind works.
What's important is what you get after you don't give up on it long enough. And that, on BEAM, is a hilariously OP superpower of effortlessly[1] parallelizing and distributing workflows. Then there are Elixir macros and the OTP supervision model. The addition of gradual typing is huge, and when the annotation syntax lands, I will definitely switch to Elixir for everything on the backend.
In any case, the only thing I can tell you is that learning Elixir is worth enduring the confusion. From personal experience, it's just a matter of learning it bit by bit over time - there's a finite set of "confusing" ideas in the OTP/Elixir/BEAM mix, and learning about some of them every other day works wonders over a few months.
[1] An exaggeration - I know! But it does make it much easier to implement parallel and distributed workflows. Recently, most of the important languages finally started getting their m-n concurrency models (from Java to Python), so the BEAM is not as much ahead on SMP, but for distribution (you can send closures to execute on different machines transparently!) it is still in a league of its own.
Check this out: https://www.theerlangelist.com/article/spawn_or_not
Written by one of the very best Elixir mentors. I believe it will dispel most (hopefully all) of your doubts and clear things up.
I'm not sure what a ghost process is? I guess something that's living beyond its usefulness / isn't supervised, etc? ... I don't speak Elixir, but you can do the equivalent of this Erlang to see everything on the node:
Then you'll know what's going on. Caveat: if you have a lot of processes, that's going to use a bunch of memory; for production you probably don't want to use erlang:process_info/2 with specific items instead of the default items. And you might don't want to output something for all the processes if you have a lot of "normal" processes that won't need to be listed.> "what if I spawn too many processes",
The default limit is 1,048,576, if you want to have more, you can add +P X to the erl command line with a bigger limit? Have your monitoring alert you when you're at ~ 80% of the limit.
> "what if this architecture is bad compared to...",
This probably addresses the real question of your too many process question. If your architecture is bad or if you spawn more processes than a good architecture would, your performance will be bad. If your architecture is really bad, you'll have a hard time solving the problems you're trying to solve. Future you will look upon your system and despair; you may also despair in the present...
Eh, you're going to make bad architecture. BEAM won't solve all your problems. But, if you've got problems it can solve, IMHO, it can be a very nice way to solve them.
> "when to kill processes",
Kill processes (or let them crash) when they misbehave. Kill them (or let them exit normally) when they've done their work and they don't have anything else to do or wait for. When you spawn a process, you'll often have a pretty good idea of the conditions that would lead to its death... Ex: if you spawn a process to handle a connection, it should probably die around the time that the connection ends. If you spawn a process to handle a request, it should probably die when the request is handled. If you spawn a process to listen for connections, it probably should die when you don't want to listen anymore. Etc.
> "whats the correct restart strategy for this"
Well... it depends. Almost never the default strategy. The default strategy is a big foot gun; at least it is for Erlang, maybe they changed it in Elixir. I need zero hands to count the number of times I actually wanted BEAM to stop because some supervised process failed 3 times in a small time frame; but it's happened to me a lot more times than that. For per connection or per request things, the appropriate strategy is not to restart at all; for other things, try to restart a few times quickly then maybe every minute or so is probably sufficient. You'll want some sort of alerting. And if the restart strategy isn't right, you can always console in and poke it.
You can always ask follow up questions for clarification, people there are generally really friendly.
But yea I know about Gleam and I did build some fourier transform stuff with Rust a while back. I like Gleam generally. I am just much much slower with FP and think its extremely unintuituve compared to, say, Go for example.
OMG, why? Why would you ever have so many processes? All of them at the same time? Are you going to animate a 3D scene and run a process for each vertex, or something?
No, I mean, if you're WhatsApp - across all nodes - then somehow maybe yes? At scale. But in normal code, slicing workloads too thinly is counterproductive, and having even tens of thousands of processes is a sign that you're slicing it way too thin. Message passing between processes is cheap, but not free. Schedulers do a good job, but rarely have more than 16 cores to work with. And so on.
You can have that many processes if you want, to be sure. But if you're struggling with it, why would you want it?
Reading your comments in this thread, I have a feeling you just didn't spend enough time reflecting on how you want to use Elixir. In effect, you also failed to consider how exactly you should learn it. For example: Elixir is a perfectly capable procedural language. Start by writing CLI tools, without spawning any processes at all. Then try to parallelize their processing. If the tool accepts a list of files as arguments, use a `Task` to compute return values for each file. Tasks are processes, but with a particular contract that simplifies their usage. Later, you can experiment with error handling and supervision by putting the tasks under a supervisor. And so on. You go from the familiar to the less familiar, with a useful, working tool every step of the way.
Sounds like there is some foundational knowledge of Elixir that you miss and everything seems more confusing than it should be. To me writing a 'server' in Elixir is orders of magnitude easier than doing it in Python, Rust or C++.
As someone else suggested, bring your concerns to the Elixir Forum and surely someone will clarify them for you
Once you taste Elixir/Erlang, there is no going back to the madness.
``` socket "/ws/:user_id", MyApp.UserSocket, websocket: [path: "/project/:project_id"]
```
Elixir gives you too much freedom on how to write something on a syntax level which really annoyed me.
I pretty frequently find myself needing to open up the source to understand what's actually going on, the docs aren't bad but it often feels like they assume a lot of existing familiarity with phoenix.
In this example, `socket` is a compile time macro and it's being called with
and what is does is register that data with the `phoenix_sockets` attribute inside the module you called `socket` from. At compile time that gets turned into a lookup inside your module, and presumable then the UserSocket module is invoked when a websocket request hits the specified path.Would you find it more clear if socket was called like this?
Or, alternatively, would it help if the endpoint was more specifically defined likeComing from other languages, I find that
being equivalent to and always takes some extra mental effort to get through, especially when there's no parenthesis. But I appreciate not having to write all the extra brackets and parens when I get going, so I think it's a fair tradeoff.Personally, I like the flexibility, but yes there are a lot of rules to keep in mind.
This is true perhaps compared to python or go, but not compared to Java, JS/TS, or some others.
> socket "/ws/:user_id", MyApp.UserSocket, websocket: [path: "/project/:project_id"]
Socket is a behavior, which is like a trait or interface. MyAppWeb.UserSocket implements the behavior. It's basically a convenience over having to write a bunch of repetitive WS or long poll handling every time you want a socket like thing. Its pretty well documented https://phoenix.hexdocs.pm/Phoenix.Socket.html.
I don't know the current state of Gleam OTP, but last I checked it wasn't great.
If you don't care about either of those things and only about types, use Gleam. But then why not just use Rust?
> I don't know the current state of Gleam OTP, but last I checked it wasn't great.
Gleam uses regular OTP, it doesn't have a distinct OTP framework separate from other BEAM languages.
This is the same as in Elixir, where macro-enabled APIs are offered, and they just wrap the regular Erlang APIs.
I wrote both Elixir and Erlang code. Erlang is just useless to me as a programming language; it has many great ideas though. I love the idea of being able to think in terms of immortal, re-usable, safe objects (Erlang does not call these objects, but to me this is OOP by Alan Kay's definition. I don't use e. g. the java definition for OOP.)
Elixir built on that and made Erlang code optional, meaning people could write more pleasent code. And here it succeeded. I am not sure why Elixir succumbed to type madness now, but the comment that "writing Elixir is like writing Erlang", is just simply not true.
Elixir is significantly better than Erlang with regard to writing code. José Valim got inspiration for Elixir from ruby, to some extent.
https://www.youtube.com/watch?v=fhOHn9TClXY
Gleam for example has issues with verbosity of decoding/encoding json whereas in Rust you derive serde and in Elixir it's just a function call away.
Elixir has a more mature ecosystem. While you can for example use Phoenix with Gleam (or some other Gleam framework) the experience just isn't the same.
The big draw with Gleam over Elixir is the typing (where Elixir is now closing the gap) and being able to compile to JavaScript (which is also what Hologram is doing for Elixir).
I prefer Gleam's typing system and the Rust-like syntax, but for now I feel Elixir is the better choice for all my web dev projects.
Apparently it is not that difficult to add different compiler backends. There was a presentation [0] recently about adding wasm support as a compiler target. The implementation was quite far along, including support for the wasm component model.
[0] https://www.youtube.com/watch?v=UQ0--ODjiDk
I have the great luck to work in many different stacks as a freelancer.
One of them is Elixir. While I am on this project for just half a year and not too many hours per week, I can say: I absolutely love this language.
It reminds me of Haskell, which I had courses on at university, and is just an absolute joy to work with.
My only gripe was that there was no typesystem. So I was eyeing Gleam (as I also like Rust very much), but as Gleam doesn't and probably never will support Ecto and Phoenix (due to it not supporting macros), it's a nogo for the project at hand.
I knew Elixir was to gain a typesystem, still this is absolutely fantastic news. Super stocked to work with this.
I am sorry for your loss here.
I think this also shows that merely copy/pasting ruby's syntax, isn't an automatic win. I noticed this before with crystal, though naturally crystal had types from the get go.Fundamentally:
should stay simple. And this is no longer the case now.(Ruby also went in error, e. g. "endless methods". I don't understand why programming languages tend to go over the edge in the last 5 years or so.)
You are commenting as if we added this now but we have made no changes to the language surface. The difference is that we now leverage these same language constructs to extract precise type information.
Two reasons I put it aside again are:
You need Beam and the Elixir. I find that really weird, because I'm used to just the language like in Python, Java, C, Rust. Not something underneath it, too.
There is no debugger. The way to debug Elixir is to print stuff to the console, like 40 years ago. No thanks.
> You need Beam and the Elixir. I find that really weird, because I'm used to just the language like in Python, Java, C, Rust. Not something underneath it, too
The beam is a VM. You get that Java requires a VM too right? It’s called JVM for a reason. And Python requires an interpreter.
> There is no debugger. The way to debug Elixir is to print stuff to the console, like 40 years ago.
That is false. https://www.erlang.org/doc/apps/debugger/debugger_chapter.ht... and you have observer. And you have a lot of other debugging tools. I hear Java has a good one and maybe it’s better (I never used it) but it’s not true there exist no debuggers for the beam.
I'd like to do step by step but I cannot plug the debugger to VScode from inside a docker container.
I am not sure what GP is objecting to.
Elixir always felt like it would be a solid functional systems programming language, so not having a compiled backend is a genuine downside.
Here's what you need to do for elixir:
Download and run the Erlang installer Download and run the Elixir installer
Here for Java: Download and run the Java SDK
And for Python: Download and run the Python installer
Note this includes installing erlang as well
While it is multiple steps, the frustration is a much more one time thing compared to the problems and frustrations you'd have using a language or its ecosystem for a long time or big project
I guess we know how he feels about TypeScript.
Download SDKMan/Jenv
Install the version(s) of Java you need for your projects
Make sure your JAVA_HOME environment variable is set
Ensure your IDEs locate the correct Java home
Compared to all that, Elixir's two installers are trivial.
And if you have a competent package manager, you can just tell it to get Elixir and it'll handle Erlang for free.