Hacker News new | ask | show | jobs
by jFriedensreich 1982 days ago
As someone who came to erlang at least in part because i hated ruby (and also rails/phoenix like frameworks) i can partially sympathise. I would however put pipe operators clearly on the pro side of elixir and also add the string handling to that list (no pun intended).

I am ultimately really happy about Elixir giving BEAM some new popularity in otherwise unreachable audiences, even though i had really hoped for something more akin to erlang2 or similar to an erlangish coffeescript.

But in the end even joe approved of elixir and i don't remember significant effort on erlang2 after initial experiments.

But the problem seems not to be syntax but more culture. I see many ruby/rails people coming into the BEAM ecosystem who bring their poisonous way to think about systems with them, even when they understand the theory about functional programming and what BEAM is about, they seem to still fall back all the time, maybe partially because the syntax is too familiar.

If is see

  defmodule TimelineLive do
     use Phoenix.LiveView
i am already fighting a puking reflex, does elixir dictate to to build a framework like this? No, but the culture bleads over.

Funnily this is a similar effect to java culture poison-swapping into javascript after class and decorator syntax was added.

5 comments

> Funnily this is a similar effect to java culture poison-swapping into javascript after class and decorator syntax was added.

This is a great insight. I remember arguing at the time that adding that stuff to javascript was stupid, and people responded arguing that it increased accessibility of the language. It’s heresy, but I think there’s something to be said for bringing people in to a language slowly so they have time and inclination to leave their past experiences at the door. Rust uses the borrow checker. Ruby has metaclasses and it’s excessive magic everywhere. Javascript used to use prototype based inheritance and callbacks. I use classes sometimes and async everywhere but we paid a price for that syntax. The price was in our previously more cohesive identity as a community around how JS was written.

Do you have actual criticisms of LiveView as a technology or are you just upset about macros?
I love Elixir (former Ruby dev) but I do find many Ruby devs will turn to macros when regular functions will do.

I haven't tried LiveView but it looks very interesting.

In this case I don't see how regular functions would do. It's a way of giving a module specific behavior in a way that fits in with the application structure Phoenix enforces. That aspect of it is very Railsy, but the way it's implemented is totally different. Elixir macros are much closer to Lisp macros than Ruby macros, because they are not object oriented. I personally find them much easier to read and understand as a result. Here's __using__ in Phoenix.LiveView: https://github.com/phoenixframework/phoenix_live_view/blob/v...

I think LiveView is a very compelling technology and I'm not sure we would have it if Elixir didn't exist, because Elixir brought a whole raft of different perspectives into the Erlang community. Erlang and Elixir are about as close as two languages can get, and I find complaints from either camp about the other silly. It reminds me of Python vs Ruby flame wars.

Maybe it's just because I'm used to C# and F# and the way they feel in the .NET world, but Elixir and Erlang both feel like different syntaxes over the exact same underlying language concepts.

I agree that Elixir macros are far easier to understand than Ruby macros since they are defined at compile time rather than runtime.

But I am against reaching for macros when the same can be accomplished using the basics: modules, functions, structs. I've seen various instances where a macro doesn't add much expressiveness over the plain old functional approach.

Granted there are many areas where macros are very powerful. Ecto is a perfect example of when macros make sense. Adds a lot of expressiveness to build queries which is worth the cost of macros.

Also should note that I love Elixir. We are using it in production to power all of our backend systems :)

> While emacs has parts written in C, that portion really exists to enable elisp.

You might not have noticed that the parts of GNU Emacs written in C exceed 250,000 lines of code.

You may be responding to the wrong comment.
To add a data point: Ruby and Elixir developer here (and Python and JS). I never wrote a macro in Elixir. All that weird quote / unquote stuff is too complicated. I get the idea (code running at compile time) but the syntax is just unwelcoming. Furthermore macros tend to make code opaque and onboarding difficult. I could wake in terror if I dream of my coworkers implementing some new functionality by adding macros instead of functions.
Agreed. My approach is to use macros written by someone smarter than me (Ecto), but refrain from writing macros myself. Regular functions very often are enough.

Macros are definitely abused, but there are some cases, fewer than you think, where they make code easier to follow.

I think the criticism may be around "use", which calls that "magic" __using__ macro on the specified module, which will do who-knows-what.
: ) Don't get me started on LiveView, i was only talking about mimicking rails mixins with the "use" macro in phoenix.

But now that you ask: The LiveView Idea to use websockets in general purpose web applications to achieve "reactivity" is an abomination, we have SSE and HTTP2 and also SPAs for that matter, which are from an operational and reasoning standpoint 1000% simpler and actually made for this kind of thing.

Websockets are for things like multiplayer shooters or collaborative drawing, why would someone think it is appropriate to use it to react to clicking a button.

So what should Phoenix use instead of a using macro to accomplish this?

>But now that you ask: The LiveView Idea to use websockets in general purpose web applications to achieve "reactivity" is an abomination, we have SSE and HTTP2 and also SPAs for that matter, which are from an operational and reasoning standpoint 1000% simpler and actually made for this kind of thing.

SPAs are hugely complicated though. Have you worked on a production React app? It's another order of complexity above anything existing in BEAM world, and the JavaScript tooling ecosystem is atrocious in comparison. Not to mention, you will quickly end up having button clicks that require network requests if you are not careful with your SPAs as well.

The whole point of LiveView is that you can develop an app with a similar amount of interactivity as a React app without having to go into the split-universe world that SPAs live in and all the attendant complexity that brings with it.

>Websockets are for things like multiplayer shooters or collaborative drawing, why would someone think it is appropriate to use it to react to clicking a button.

People said JavaScript was just for making ad banners flash 20 years ago and now it's the most popular language on the planet. I don't think a technology's current uses are a good predictor of what it is most useful for. See also: the web itself becoming an application platform, while it started off as a hyperlinked document retrieval system.

With respect, web sockets are a tool. If you think it is inappropriate to use a tool a certain way then your are obviously free to feel that way, but it seems a little “get off my lawn” to be upset with other folks finding creative uses for it.

My team is using live view and accomplishing incredible things with far less time and effort than we would need using other SPA technologies.

I have no idea how you can assert SSE, HTTP2 and SPAs are 1000% simpler when my experience over the last two years has been the exact opposite. Live view is exceedingly simple to work with.

Can you expand a bit more on what you mean by Ruby culture poisoning Elixir? I've never used Ruby in my life and that part was entirely lost on me.
The most obvious example is thinking in classes and then mixing in behaviour from "magic" modules through inheritance and multiple mixins.

Phoenix for example uses the "use" macros like in my post above to mimic this horrible behaviour. In an erlangish approach you would mainly explicitly import and export functions from clearly specified modules.

If you specify a -behaviour (i know its not a perfect analogy but illustrates the point) that would not magically add extreme amounts of code paths but only force you to export the correct functions for what you try to do.

While “use” resembles mixins, it is a much restricted version of whatever you can do in Ruby thanks to lexical scope, closed modules (via open classes), and immutability.

In practice “use” is closer to Ruby’s refinements, which is something that never caught in Ruby because they were too restricted.

You are right that you don’t see this in Erlang but you don’t see similar high level frameworks in Erlang either. Many don’t care for them but many also find such tools an essential part of their toolkit.

What's the advantage that Elixir has over Erlang?
Beyond the very subjective syntax, docs and whatnot I would say adoption. At this point there are a lot of Elixir libraries and frameworks that are built with Elixir and are quite popular that might not work from Erlang. Elixir is a steadily growing language with a strong enthusiastic following. Erlang is super cool but on comparison does seem to remain quite niche.

Some would say the Elixir advantage is marketing. I think there is more to it with an approachable web framework, an interesting DB library, pushing into current trends around SSR with LiveView even before Rails got Hotwire.

There's a lot of interesting work around Elixir, I've blogged about why it has me hooked a few times. Things like the Nerves project, Membrane Framework and Scenic are very cool in my book.

I would say there are two main differences and then some smaller more subjective ones. The main two:

Syntax - Elixir has a more familiar looking Ruby derived syntax, plus some little extras like the pipe operator. In practice, once you've used either Elixir or Erlang for a few hours the syntax difference isn't that important, you get used to it quickly. That first few hours is important though - if someone doesn't get through them they're not going to learn the language. Elixir makes that a bit easier, gives you one less thing to think about during early learning and helps adoption. There are some other niceties like the pipe operator and for comprehensions that can help organise code, but you could live without them.

Macros - Erlang has no capability for metaprogramming. Macros do need to be used sparingly and carefully as they can obscure what's going on (Erlang is very literal and that's a good thing). When used well they can be a great help at avoiding boilerplate. Elixir itself uses them for things like GenServers and supervisors. This does things like providing default child specs and allowing them to be overriden in the GenServer. Phoenix makes heavier use of them, mostly seamlessly (and it's a great benefit). The router is the only place where they're slightly confusing as the magically macros generate some extra helper functions. The mix tasks (mix phx.routes) show these generated function names, but it's not the same as having a definition in a file.

Other hand wavier things include better tooling (although a lot of the improvements are now present in Erlang tooling as well) and better documentation.

It is mostly a matter of taste, but Elixir has pipes, decent string primitives, a bit less boilerplate, probably some edges nicer in tooling or standard library, i would say thats about it, both run on the BEAM VM and compile to similar BEAM bytecode.

The killer feature is approachability of syntax. Every single developer i talked to complained about erlang syntax unless they did prolog before or already were an erlang developer. I prefer it but me and OP seem to be the few people who do these days.

There are parts of the OTP that really struggle with erlang's "purity". The best example I can give is the tftp module, which I wanted to use to do pxe booting. After about 2 days of trying to track the spaghetti callbacks back and forth between the tftp, tftp_engine, tftp_lib, and the callback module to fix a bug and commit a change to OTP, I gave up and wrote my own TFTP module in Elixir. With a very conservative amount of `use` macros, to help organize calls it came out beautifully: https://hexdocs.pm/trivial/Trivial.html

My point being, is that sometimes some patterns (in the case of tftp, a really awkward java-esque factory pattern) do not do so well in erlang's pure functional system and if you instead pass behaviours with `use` you can do much better and have saner code.