4 ms·
How tricky is it to simulate mutable variable semantics in a fundamentally-immutable language?
by pmarreck 5y ago
How tricky is it to simulate mutable variable semantics in a fundamentally-immutable language?
- qsort 5y agoConceptually, not at all: you simply create a copy of whatever data structure you are keeping in memory to bind names to values. (Notice that you are probably already using something like this to correctly resolve scopes -- so it's not like it's more work, either). In practice, I guess it depends on how much you care about performance and how 'smart' the functional runtime is. But if you're not writing a toy interpreter, you probably also need bytecode compilation, etc.
- dnautics 5y agoerlang is not fundamentally immutable. It's just "locally immutable". You can't mutate a variable. A function call can have side effects, just it can't change its arguments -- but if it's argument is, abstractly speaking, a reference [0], it can effect a change on the abstract contents of the reference. Using this mechanism there are TONS of escape hatches using function-called "abstract references". [0] maybe the reference refers to a file, network socket, a url over the internet, which work in the obvious way. It could also be things that erlang gives you for free: the id to another process (thread) to which you send an instruction, "change your internal data", a "process dictionary" (think: thread-local-storage ish), "ets tables" (think: a redis/memecached you get for free"). If you want to go deeper, it could even be "a wrapped, refcounted, C pointer to raw memory", there are ways to do this safely that the native interface gives the curious low-level programmer.
- Jtsummers 5y agoA couple of ways: Function parameters with a "state" structure, processes (and function parameters). A common pattern is something like this: loop(N) -> Next = do_stuff(N,...), loop(Next). That would be the first pattern. So you could have a state variable (or set of variables) which are changed on the next iteration. Since Erlang does tail call elimination, this is the same as a while/for loop in other languages but does not have explicit mutation. You can also return the new state and thread it through various function calls. With processes, you can do something similar: counter(N) -> receive Pid -> Pid ! N, end, counter(N+1). user(Count_PID) -> Counter_PID ! self(), receive N -> io:format("~p times~n", [N]) end, user(Counter_PID). (probably typos above, haven't done Erlang in a while) Between these two, and more advanced use of language modules, you can provide mutable state (of a sort). EDIT: Also worth noting that, before getting to anything beyond the basic syntax, both records and maps permit updates rather cleanly. For instance, with a map you might have: Map = #{a => 10} Updated = Map#{a := 42} So if you have a register map and do something like (again, been a while so forgive syntax errors): update_register(Rs, Register, Value) -> Rs#{ Register := Value }. The rest of the map is left unaltered, only the bit that needs to be updated gets changed. And Erlang/BEAM is careful about not making more copies than needed so in theory (at one point I studied these details, I've forgotten the specifics) this should result in very little new memory being allocated for the updated map.