Live data from Hacker News

Elixir for Ruby developers: the three most important differences

phoenixonrails.com

71–78 of 78 posts

Re: Elixir for Ruby developers: the three most important differences

#72

Earlier quoted context omitted.

Just wrapped up a live translation feature that watches an HLS live stream, live transcribes with whisper and then translates into 18 languages. Heavy use of OTP: supervision trees for each stream, ports for managing ffmpeg and receiving audio, async tasks for concurrently submitting chunks to translation API, etc. Transcription and translations provided in real-time via LiveView to about 4,000 viewers. Little over 3…

That's really cool. Managing ports is something I've done in elixir yet, but managing ffmpeg through elixir could be very useful for a few things in my current area of focus. Can you share any details of how that works? E.g. do you operate on a single HLS chunk at a time, or can you get ffmpeg to separate a continuous audio stream, etc?

davidw is right that ports are somewhat limited, but I haven't had much trouble doing what I need with FFmpeg in particular. I used the bash wrapper from the docs for Port [^1] and it has worked well.

When a stream starts I start a supervisor that then starts a GenServer to manage the port. On init a port is started for FFmpeg (using the above bash wrapper) with args that sends 16-bit PCM audio back to the port through the `handle_info/2` callback.

When a new live HLS segment is downloaded by FFmpeg the entire segment's audio is sent to the GenServer all at once (could be a few handle_info/2 calls, but it happens quickly). Since I want to work in small fixed chunks, I send the segment's audio to an AudioBuffer GenServer (started as a sibling under the same supervisor). This buffer uses binary pattern matching to segment the audio in chunks exactly 2 seconds long while keeping any remainder in the GenServer's state for the next buffer event. I then send the chunks to another ChunkBuffer GenServer that pops chunks at 2-second intervals for processing.

Since everything is supervised, if (when...) FFmpeg crashes the supervisor just restarts it. Meanwhile, the audio in the buffer is still processing and nothing goes down. There might be a duplicate word or two in the transcription if the restarted port processes a segment again, but everything keeps running smoothly.

For even more reliability, I have the application running clustered across four locations in the US, EMEA, and APAC using libcluster[^2]. The stream supervisor is started under a Horde.DynamicSupervisor[^3] with a custom distribution strategy. The strategy prefers the region closest to the company HQ, but if it goes down, the processes will be restarted in another region.

[^1]: https://hexdocs.pm/elixir/1.13.4/Port.html#module-zombie-ope...

[^2}: https://github.com/bitwalker/libcluster

[^3]: https://github.com/derekkraan/horde

Re: Elixir for Ruby developers: the three most important differences

#73
post #27

The article section about interpreted vs compiled is incorrect. .exs files are compiled just like .ex files are. The only difference between `elixirc` and `elixir` is that the former creates an artifact on disk, and the latter does not. See https://medium.com/@fxn/how-does-elixir-compile-execute-code... . Also, Ruby is as compiled as Elixir is. Compiled vs interpreted is blurred the moment you compile to bytecode run…

To add to your excellent points: Elixir is compiled ahead of time while Ruby bytecode is generated as the code is loaded, which is probably the source of confusion in this case. This leads to differences such as Ruby meta-programming happening at runtime, Elixir’s at compile time. The Elixir compiler (the Erlang compiler really) can also afford to do more work at compile-time which then leads to different approaches…

I'll add that Ruby meta-programming has 2 pseudo-phases(at least in my mind): load time and 'true' run time. They're both technically run time, but smth like a has_many method in rails ActiveRecors runs once when the code is loaded, defines some other methods and doesn't run again while the process is running, normally.

You could also be defining methods pretty much whenever, for instance in response to user input, mutating the state of the process from that call onwards.

The former is much, much more common than the latter.

e. A blurring of the 2 is something like ActiveRecord defining field accessors dynamically once a DB connection is eatablished. You connect to the db and now your User model has email, first_name etc methods, unless you'd defined them already.

I'm guessing nothing quite like this could exist for Ecto(I vaguely know this isn't a super good comparison, Ecto is quite different from AR from what I remember).

That being said even that would still happen as part of a prod app's 'loading' phase so to speak rathet than during a request cycle.

Re: Elixir for Ruby developers: the three most important differences

#74
post #57
post #53

Earlier quoted context omitted.

I don’t see one?

> Last updated • Jul 23, 2023 after the Learn Phoenix Fast red box. However the convention is to put dates at the beginning of an article. Readers shouldn't have to make an effort to look for them. This reminds me when I had to look for how to check for inequality in Lua after I wrote != and it didn't work. didn't work too.

Oh, I was talking about the joyyo.app link that Mizza posted.

Re: Elixir for Ruby developers: the three most important differences

#75
post #27

The article section about interpreted vs compiled is incorrect. .exs files are compiled just like .ex files are. The only difference between `elixirc` and `elixir` is that the former creates an artifact on disk, and the latter does not. See https://medium.com/@fxn/how-does-elixir-compile-execute-code... . Also, Ruby is as compiled as Elixir is. Compiled vs interpreted is blurred the moment you compile to bytecode run…

I stand corrected! I've updated the article now (and have added a link to that informative Medium post.)

Thanks for keeping me factual.

Re: Elixir for Ruby developers: the three most important differences

#76

I love Elixir and I have worked with it in past 2 years. (Specially in China) But I have been free because the company was broken. I really want to keep writing Elixir, are there any remote job opportunities here? P.S. I also have some experience of React/Nextjs, Ruby and Scala.

Here's something that might interest you :) https://remote.com/openings

Re: Elixir for Ruby developers: the three most important differences

#77

Earlier quoted context omitted.

To add to your excellent points: Elixir is compiled ahead of time while Ruby bytecode is generated as the code is loaded, which is probably the source of confusion in this case. This leads to differences such as Ruby meta-programming happening at runtime, Elixir’s at compile time. The Elixir compiler (the Erlang compiler really) can also afford to do more work at compile-time which then leads to different approaches…

I'll add that Ruby meta-programming has 2 pseudo-phases(at least in my mind): load time and 'true' run time. They're both technically run time, but smth like a has_many method in rails ActiveRecors runs once when the code is loaded, defines some other methods and doesn't run again while the process is running, normally. You could also be defining methods pretty much whenever, for instance in response to user input, m…

Another difference between the two comes from Ruby having open classes, so there is no callback that says "no further changes will be made to this class". This means you need to postpone some meta-programming to certain events, like Rails telling the app has done initializing, or until something is invoked for the first time.

So you are right there are distinct phases but they are established by convention and practices. And sometimes it is different between dev and prod (lazy loading vs eager loading). Or at least it was back then. :)

> I'm guessing nothing quite like this could exist for Ecto

Correct. :)

Re: Elixir for Ruby developers: the three most important differences

#78

Earlier quoted context omitted.

That's really cool. Managing ports is something I've done in elixir yet, but managing ffmpeg through elixir could be very useful for a few things in my current area of focus. Can you share any details of how that works? E.g. do you operate on a single HLS chunk at a time, or can you get ffmpeg to separate a continuous audio stream, etc?

davidw is right that ports are somewhat limited, but I haven't had much trouble doing what I need with FFmpeg in particular. I used the bash wrapper from the docs for Port [^1] and it has worked well. When a stream starts I start a supervisor that then starts a GenServer to manage the port. On init a port is started for FFmpeg (using the above bash wrapper) with args that sends 16-bit PCM audio back to the port throu…

Absolutely fantastic write up - thank you so much. I will go away and do some further reading!
Post reply on HN