Live data from Hacker News

Unpacking Elixir: Concurrency

underjord.io

31–40 of 138 posts

Re: Unpacking Elixir: Concurrency

#31
post #5

Earlier quoted context omitted.

What do you find more difficult? The achieving tasks in it or the getting paid to do so part?

There’s definitely a bit of a learning curve if you’ve never written in a language where everything is immutable. Certain problems have to be approached in a fundamentally different way if you can’t mutate state. Fortunately there’s Agent/OTP, but again, pretty different than other (or popular with beginners - js/python/java) languages.

You really shouldn't be using agent for mutable state. Kind of annoying that the docs push you to that. Agent is most useful when you need to tie the transient lifetime of some state with something else (think: compilers). Otherwise, it's generally better to use a database, (or ets, if you need performance), GenServer/gen_statem if you need reactive events attached to the state. Agent is just a wrapper over GenServer so it is strictly worse in terms of performance, and honestly, slightly hard to grok as to where actions take place (and thus, who is responsible for failures)

Re: Unpacking Elixir: Concurrency

#32

Earlier quoted context omitted.

all the C family all the JVM languages all the .Net languages Rust Ruby Python etc.

Thanks. What's the Ruby example? It's the one I'm most familiar in.

   irb(main):002:0> ["GFG", "GFG", "GFG", "GFG"].class
   => Array
   irb(main):003:0> {1 => "CFG", 2 => "CFG"}.class
   => Hash
   irb(main):006:0> {1 => "CFG", 2 => "CFG"}.keys.class
   => Array
   irb(main):007:0> {1 => "CFG", 2 => "CFG"}.values
   => ["CFG", "CFG"]
   irb(main):008:0> {1 => "CFG", 2 => "CFG"}.values.class
   => Array

Re: Unpacking Elixir: Concurrency

#33

Earlier quoted context omitted.

Vs which other languages - do you have examples of better patterns?

all the C family all the JVM languages all the .Net languages Rust Ruby Python etc.

Python lists are generally not contiguously allocated memory (I think it might be if you are storing integers less than 255), don't be fooled. That's why you need numpy.

Re: Unpacking Elixir: Concurrency

#34
post #12

The deal-breaker for me with Elixir has always been the lack of real Elixir vectors rather than the crappy Erlang array library. Yes, I know you can use a Map with numeric keys but that's not the same.

So what do you use real arrays for? For 80% of programming lists are just fine. Are you doing cpu-bound numerical calculations?

Re: Unpacking Elixir: Concurrency

#35
post #14

Earlier quoted context omitted.

What would be the difference between a "real vector" and a Map with numeric keys?

Maps are hashtables, Vectors are a contiguous area of memory where each element can be access by index referring to a specific address in that memory. Vectors are usually also homogeneous about the data they hold, because each element should occupy the same fixed amount of memory, such as i*item_size gievs you back the offset of the element i in memory. Anyway, in Elixir you can use the Erlang's :array module

I'm pretty sure erlangs :array is just a skin over tuple, so it's ~O(1) but not contiguous. might be O(log n) for dynamic arrays. The only truly array datatype in Erlang is :atomics

Re: Unpacking Elixir: Concurrency

#36

Unlike javascript, elixir makes you write synchronous code, e.g. def process_items(items) do items |> Task.async_stream(&Processor.process/1, max_concurrency: 2, timeout: 7000, on_timeout: :kill_task) |> Enum.to_list() end Semantics of regular and concurrent code is the same

Node has kind of wrecked the term "synchronous". It convinced many people that "synchronous" also entails "only thing running on the processor at the time" and "if you're 'synchronously' waiting on a file read the entire process is waiting". So when you say "Elixir/Erlang/any threaded language 'makes' you write synchronous code", to the people who most need to hear this, those steeped in the async way of the world, you confuse them because to them that means your language can only do one thing at a time.

I'm not saying I'm happy about this or that the meaning has "really" changed; I'm just saying from experience you don't really want to phrase it this way because you only reach those who already know.

Re: Unpacking Elixir: Concurrency

#37
post #3

I think a few code example would have been nice. The thing I like about concurrency in Elixir is that it's there if you need it but it's mostly not mandatory in your code, compared to Javascript where its kind of imposed on you even when it's an hindrance. I remember one trick I used to do with LiveView on click events etc was to put all async (or "asyncable") code in a spawn function, which would speed up the return…

Yes exactly! This is super important for people new to elixir. The vast majority of the time, you don't even need to know that there is even any concurrency. You can write entire sophisticated Phoenix apps and never need it. But when you do eventually need it, it's there and it's wonderful, once you grok the pattern.

Agree. That's a big advantage of thread-based concurrency [0] vs async/await. The decision of whether to do things sequentially or concurrently sits with the caller, not the callee. Which means it's there where you need it and out of the way when you don't. With async/await, the decision is made by a function implementer somewhere down the stack, quite probably in 3rd party code. As the caller you get no choice because coloured functions [1]. You have to deal with concurrency whether your app needs it or not.

--

[0] By thread I mean the general concept, not specifically OS threads. In this case it includes the fine-grained processes offered by the BEAM VM that underpins Elixir and Erlang.

[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...

Re: Unpacking Elixir: Concurrency

#38

Unlike javascript, elixir makes you write synchronous code, e.g. def process_items(items) do items |> Task.async_stream(&Processor.process/1, max_concurrency: 2, timeout: 7000, on_timeout: :kill_task) |> Enum.to_list() end Semantics of regular and concurrent code is the same

This. I don't want to be in an event loop by default.

Hate to break it to you but in the BEAM you're always in an event loop.

Re: Unpacking Elixir: Concurrency

#39
Disckaimer: I have never used Elixir in any serious capacity, but I have done a good chunk of Erlang.

Concurrency in Erlang sort of frustrates me...not because it's bad, but because when I use it I start getting pissed at how annoying concurrency is in nearly every other language. So much of distributed systems tooling in 2023 is basically just there to port over Erlang constructs to more mainstream languages.

Obviously you can get a working distributed system by gluing together caches and queues and busses and RPC, but Erlang gives you all that out of the box, and it all works.

Re: Unpacking Elixir: Concurrency

#40

Earlier quoted context omitted.

all the C family all the JVM languages all the .Net languages Rust Ruby Python etc.

Python lists are generally not contiguously allocated memory (I think it might be if you are storing integers less than 255), don't be fooled. That's why you need numpy.

I'm talking about Python arrays

https://docs.python.org/3/library/array.html

   >>> a = array('B', [1, 2, 3, 4, 5])
   >>> str(a.buffer_info()[1] * a.itemsize) + " bytes at address #" + str(a.buffer_info()[0])
   '5 bytes at address #4379640336'


   >>> b = array('l', [1, 2, 3, 4, 5])
   >>> str(b.buffer_info()[1] * b.itemsize) + " bytes at address #" + str(b.buffer_info()[0])
   '40 bytes at address #4380812752'
Post reply on HN