11 ms·
My favorite Erlang program (2013)
- refactorworks 3y agouniversal_server() -> receive {become, F} -> F() end. Honestly I don't fully appreciate the power of this universal server. Can anyone help?
- brokencode 3y agoNormally you need to know ahead of time what your server is going to be used for and write the code in advance. But with Erlang, you can have a distributed network of Erlang servers where the server is a generic computing resource that can do anything the client wants. The code actually comes from the client. No need to get your system administrators to install some binary on all the machines. You simply pass the function along and the remote machine calls it.
- refactorworks 3y agoThanks! I get it — no need to install binaries of specific servers.
- rramadass 3y agohttps://news.ycombinator.com/item?id=37415159 https://news.ycombinator.com/item?id=37415159
- nesarkvechnep 3y agoDouble check whether you think of the correct abstraction level. The BEAM is more like the JVM than Kubernetes.
- zbentley 3y agoOne of the most interesting characteristics of the BEAM, in my opinion, is that it's similar to both. As a memory-managed bytecode runtime it's similar to the JVM, and as a distributed process orchestrator+discovery and RPC system, it's similar to Kubernetes. I often wonder what would have happened if the "BEAM renaissance" (driven largely by the birth of Elixir and associated tools) had happened a decade earlier, before Kubernetes became the de-facto standard for ad-hoc distributed computing in web software.
- zaphirplane 3y agoA few people here are liking the idea, isn’t this the exact definition of arbitrary code execution (exploit) It’s possible people are showing the capability of BEAM thou
- coldtea 3y agoIt's the definition of any distributed system (or even single server) where you can deploy code, whether it's Erlang or a kubernetes cluster you setup. Is pushing new code for your server to run "arbitrary code execution"? I guess we can call it that. Is it an exploit? Depends if the code comes from some random person on the internet from mechanisms that you don't intend for pushing new code to run (e.g. through a buffer overflow on your server or XSS), or if it comes from yourself through your official mechanisms.
- jerf 3y ago"Arbitrary code execution", yes, "exploit", no. You need to be "inside" the BEAM cluster and a member of the BEAM cluster to do this. That is not something you hand off to end users, just as you do not normally hand end users direct access to your database socket or other such resources. In Raymond Chen's terminology [1], if you're sending Erlang terms to the BEAM cluster, you're already on the privileged side of the airtight hatchway. [1]: https://devblogs.microsoft.com/oldnewthing/20060508-22/?p=31283 https://devblogs.microsoft.com/oldnewthing/20060508-22/?p=31...
- brokencode 3y agoOnly if you allow untrusted clients to join to the cluster. To really make use of something like this, you’d have to control the clients rather than opening it up to anybody on the public internet.
- macintux 3y agoI may be answering the wrong question, but I’ll give it a shot. Erlang’s architecture is unusual; both the virtual machine and the language are built around the idea of tiny processes operating concurrently, each process running in an infinite loop waiting for incoming messages to interpret. This allows a process to become whatever code you send it. If you need a process to control a microwave, and then run some quantum computations, and then predict the winner of tomorrow’s football game, you just send it the code it needs for each operation and it happily does so.
- notRobot 3y agoAh, so TL;DR is that each process evals the supplied code and returns the output to the caller?
- macintux 3y agoSlightly different in the low-level details, but conceptually accurate.
- Jtsummers 3y agoThe universal server may or may not return anything. It just executes whatever is passed to it. In Erlang there are two ways of "calling" (in quotes for a reason). There's conventional function calling which is the regular synchronous style that we all know and love: f(10). This will produce a result and return it to its caller. The other isn't really a call, it's "sending": Pid ! 10. Some process id has been sent the value 10. It may be on this same node, it may be on another node, I don't have to care (sometimes I do though). This is asynchronous. Once a send is done the sending process will continue on (perhaps even terminating). At the other end of the send is a receive (hopefully, otherwise somebody's queue is getting filled up...): receive N -> ... % do something with this value end. In the case of `universal_server` we don't know what it will become, it's just going to execute whatever 0-ary function is passed. That function may or may not include a "return" (sending a value back to the origin). It could also just terminate the universal server. Or it could temporarily convert the universal server into something else and then become a universal server again.
- Jtsummers 3y agoYou execute that on any node in your system and send it a message `{become, fun some_function/0}`. Once it receives that conforming message (a tuple of two items, the atom `become` and a 0-ary function) and that node will stop being a "universal server" and become whatever process "some_function" describes. And in his case he had access to some 9000 computers. If each was running at least one Erlang node and each node was running a universal server, then with a very simple program he could write a function, serialize the function, and distribute the function to his 9k+ running universal servers and turn them into 9k+ specialized servers.
- softirq 3y agoIf you are familiar with Go, it's similar to a goroutine that waits for someone to send it an anonymous function to start executing. In Erlang, you solve problems by spawning lots of processes, and most processes are waiting to accept messages. One critical difference is that in Erlang, processes can run on remote machines, seamlessly. This process accepts a message which is a tuple containing the atom become, which is basically just an enum, and a function. Another process, such as the Erlang shell, can send this tuple message with the function to this process at any time so that it "becomes" that function. What he is saying is that you can swap out the logic of this waiting loop with whatever protocol logic you want. His example was that he had a fleet of machines that were running this loop, and he sent all of them a function that implemented a gossip protocol. But he could easily send them all another message that turns them all into BitTorrent clients. Joe was an absolute genius and an extremely kind person. I had the honor of meeting him once. Erlang is still one of the most beautiful technical creations I've ever encountered. It really does make you see concurrency in a whole new way.
- thakoppno 3y ago> One critical difference is that in Erlang, processes can run on remote machines, seamlessly. Is there a concise way to explain how Erlang achieves this property?
- deleted 3y ago[deleted]
- querulous 3y agoit's not as mysterious as it sounds. every data structure (including modules and anonymous functions) has a binary serialization and every erlang vm is also an rpc server that can receive arbitrary data -- including whole programs -- and execute them. your vm of course needs to know about the remote vms to do so but that's where the rudimentary clustering mechanism in erlang comes into play
- thomasfortes 3y ago
- agundy 3y agoThis universal server is a process listening for a message with a function and then it executes that function here the function is just a different infinite server turning a running process into something new. I think it shows off the power of erlang processes and the ability to pass functions to replace running processes with new behavior without changing pids.
- h0l0cube 3y agoTo explain further, the `receive` keyword is a bit like a switch statement (but actually a pattern matcher) for incoming messages. Here they made a new server that takes an return process (From) and a number (N). The exclamation point sends the result back to the return process. factorial_server() -> receive {From, N} -> From ! factorial(N), factorial_server() end. factorial(0) -> 1; factorial(N) -> N * factorial(N-1). This code then spawns the server, sends a message to that server to become a factorial server, then tells that server to send it back a message with the factorial of 50. It then specifies it's own message listener that takes whatever it receives and returns it. test() -> Pid = spawn(fun universal_server/0), Pid ! {become, fun factorial_server/0}, Pid ! {self(), 50}, receive X -> X end. A couple of the major advantages of Erlang its distributed parallel nature, and also hot code update. Which happens in `Pid ! {become, fun factorial_server/0}` where it overides the receive loop of universal_server with that of the factorial_server. Though I think proper hot code update doesn't work like this
- coldtea 3y agoAnd /0 is the local vm?
- chrisoverzero 3y ago`/0` refers to the arity of the function.
- codemonkey-zeta 3y agoIt's the arity of the function. In Erlang there are no variadic functions, but functions with different arities can have the same name, so universal_server/0 takes no arguments, fib/1 takes 1 argument, fib/2 takes 2 arguments, the second arg may be the accumulator for a recursive Fibonacci, for example, and fib/1 may call fib/2 as an implementation detail.
- jay-barronville 3y agoBasically, the idea is that it can become any type of server you’d like. The actual function to run the server is passed by the client and the Erlang process effectively morphs into that server after constructing it using the provided function. When you consider Erlang’s hot reloading abilities, this simple architecture becomes even more powerful. Another way to look at it is that the Erlang process is just compute waiting for work to do and the work is to run full-blown servers. Pretty neat.
- jay-barronville 3y agoI was typing this answer on my phone and I didn’t realize several folks already responded. Move along, nothing to see here. Haha.
- xvilka 3y agoIs the same trick possible with other BEAM languages? E.g. Elixir?
- hmmokidk 3y agoYeah. I don’t see why not.
- qohen 3y agoYes. Here[0] is a gist showing it done in Elixir -- as you'll see, it looks very similar to the Erlang code: [0] https://gist.github.com/mndvns/80b00cf67d418e8359fb5566b80aeb4c https://gist.github.com/mndvns/80b00cf67d418e8359fb5566b80ae...
- ungamedplayer 3y agoHere is an aggressive worm/virus that exploits this exact mechanism. https://github.com/wmealing/Elixir-virus https://github.com/wmealing/Elixir-virus
- rramadass 3y agoYes; it is a property of the "Erlang Run Time System"(ERTS)/"VM"(BEAM) - https://news.ycombinator.com/item?id=37415159 https://news.ycombinator.com/item?id=37415159
- javajosh 3y agoYou can achieve the same goal, if not so elegantly, if you define a node process that processes HTTP POSTs by evaling the body of the request to replace the previous handling function. In practice you'd quickly want to post a function that behaves normally. However you could also define a function that does something "normal" but has a code path for continually redefining the function. As exotic as this sounds, this is very similar to what web-browsers do with script src tags, especially from 3rd parties. The page is saying "Hey let me eval a function that can do whatever it wants in this context. I trust you!" Most webdevs don't consider this a threat vector!
- octacat 3y agoIt is a nice example, which would be rarely used in the real systems. Could be used for understanding how hot-code reloading could be implemented. Real usage could be if we have some big state and wanna apply some operations on it, we could send "{execute, F}" into the server and pass code (i.e. a simple reference) instead of data.
- hmmokidk 3y agoMy favorite Erlang programmer. RIP Joe.
- rdtsc 3y agoIf the server closure F, besides it's own messages can also receive a `{become, F}` message, then you can then keep changing the server to something new again and so on. Completely unrelated, but I remember talking to Joe at one of the Erlang conferences. He was always excited about technology and always happy to chat with anyone. He was dismayed how Windows had gotten worse and less usable over the years, and how one day we won't be able to browse our own files on it until we sit and watch advertisements for a while to unlock them. Joe was nearly right! Sure enough, years later I hear there are ads in Windows 11 and you have to go out of your way to remove them. Not quite the same yet, but by Windows 13 I am sure we'll get there.
- vector_spaces 3y agoI use Linux at home, and use Windows for work. Outside of the ads, I've been most surprised by 1. My Windows machine has a lot more of the weird issues I used to see on Linux like 6-7 years ago. Like nothing showstopping. More like quality of life reducing stuff, like the volume in one headphone will be far louder than in the other, despite the mixer showing them the same. Or weird crashes in applications (specifically with Microsoft software). Or Windows documentation pages not resolving in any browser, forcing me to use an archive site to view them. Or my laptop occasionally not detecting my external monitor after detecting it fine for weeks. Or the monitor will flash occasionally 2. Less surprising, more dismaying: Office software by default tries to get you to save files to the cloud instead of locally. Yes, having your files everywhere across all devices is nice, I know, but I would rather have my files local first, with cloud backup, rather than have my files in the cloud first. I'm old fashioned, I guess 3. WSL is pretty jank, but it is probably the only reason Windows is usable at all for me More topically: I've always heard that about Joe being a wonderful human being, and it makes a lot of sense. When I was first teaching myself to code, I came across his book on Erlang, and it had a huge impact on me. I loved the playfulness, humility, and imagination that he brought to his work. Even though I hardly ended up writing any Erlang, reading his writing changed the way I think about so many things related to code. It's clear he cared a lot about pedagogy. I have a math background, and wish that both math and software engineering and CS culture embraced playfulness and humility more
- 3y ago
- monksy 3y ago[flagged]
- weatherlight 3y agoI spend a lot of time trying to explain why the BEAM is special and why concurrency in Erlang/elixir/etc is special when juxtaposed next to Go's or Java's concurrency story (now that Loom is right around the corner). From now on I'll just link them to to Joe's favorite Erlang program and this HN thread.
- tombert 3y agoMy latest obsession has been the Julia language, which seems to borrow a number of ideas from Erlang in its distributed model. It's reasonably easy to replicate this kind of "instant-server" that magically works across multiple nodes. I don't know for sure, but I would be surprised if Julia's Distributed module wasn't at least a little inspired by Erlang. I absolutely love Erlang, and I wish I had more of an opportunity to use it, but it's nice to see that some of its concepts are bleeding into other platforms.
- shele 3y agoAh, that's great, sounds like we are allies, for example when arguing for "let it crash" and other take aways from the Erlang error reporting philosophy in the Julia community.
- orangea 3y ago> What I ended up doing was making some scripts to install empty universal Erlang servers on all the Planet lab machines (pretty much like the code in this article) - then I set up a gossip algorithm to flood the network with become messages. Then I had an empty network that in a few seconds would become anything I wanted it to do. But you already had an empty network that in a few seconds would become anything you wanted it to, that's how you installed empty universal Erlang servers on all the computers.
- jandrese 3y agoThe difference is I think that you hand off managing the distribution of the code to the Erlang VM, making it much easier to distribute anything else. Of course security guys are going "Wait, every machine has an open RPC channel that will blindly execute code to every other machine???"
- stephenr 3y ago> security guys are going It's a bit concerning if it's only "security guys" seeing issues with this approach.
- toast0 3y agoA cluster of Erlang nodes in distribution don't have a security boundary once they're connected. Neither do two threads in the same process in a traditional system. If that's an issue, and I can see why it might be for some, you'll need to use something other than Erlang distribution to connect your cluster. You can patch out the explicit rpc server, but I'm not sure that you can patch out receiving functions (maybe removing the code that deserializes them from external term format would work?), and you'd need to audit the whole thing to ensure nothing ever calls a function it receives.
- stephenr 3y agoI'm not a security guy, so I can't speak for what their concerns would be. I think my concern would be more in the realm of, you're inherently relying on whatever peer authentication system BEAM uses (and it being bug/exploit-free), to determine whether an RPC call can suddenly inject new code into a running system. In my experience good security is a lot like an onion - there's lots of layers and generally some crying. If we consider a "non-BEAM" system that runs on say JVM, or even a "scripting" runtime (e.g. PHP, Python, Ruby, NodeJS) - you typically have a number of elements that (can) contribute to making the code that runs, relatively immutable outside of a deployment event (e.g., restricting access to ssh/sftp/etc protocols; varying types of filesystem permissions; readonly volumes; container filesystems; etc) Do other systems have similar "run arbitrary code" vulnerabilities? Sure. Allowing uploaded content to be executed by the PHP runtime is a classic example. But there's almost no legitimate purpose for this, it's almost certainly a configuration error/mistake, and it's relatively trivial to prevent it completely.
- dang 3y agoRelated: My favorite Erlang Program (2013) - https://news.ycombinator.com/item?id=31639382 https://news.ycombinator.com/item?id=31639382 - June 2022 (19 comments) My favorite Erlang program (2013) - https://news.ycombinator.com/item?id=22413029 https://news.ycombinator.com/item?id=22413029 - Feb 2020 (54 comments) My favorite Erlang Program (2013) - https://news.ycombinator.com/item?id=12396420 https://news.ycombinator.com/item?id=12396420 - Aug 2016 (38 comments) My favorite Erlang program (2013) - https://news.ycombinator.com/item?id=8807660 https://news.ycombinator.com/item?id=8807660 - Dec 2014 (2 comments)
- p-e-w 3y ago> Dean was doing an Erlang project so he asked “What example program would best exemplify Erlang“. I wish such a canonical example was easy to find for every programming language, particularly those on the boundary between mainstream and exotic languages. Most languages present themselves by waxing philosophical about HoTT, zero-cost abstractions, or parametric types. Show me an example where your language is clearly better than Python, Ruby, TypeScript, C#, or Rust, because those are the languages to beat, and they already have the entire infrastructure set up so unless you can demonstrably outperform them in some way, it's probably not worth my time to take a deeper look.
- almost 3y agoBut what if it’s better for complex tasks or large programs? I also wish language home pages would show examples of what makes the language special right away but it’s not always that easy.
- daitangio 3y agoThere is also this lesson from Joe, about how to write a basic server in Erlang: https://gioorgi.com/2015/erlang-lesson1/ https://gioorgi.com/2015/erlang-lesson1/ I transcribed and explained a bit more: it shows the power of an async language like Erlang/Elixir compared to other ones. Sadly, it is little used nowadays
- kitd 3y agoExcuse a noob question, but what gets transmitted over the wire as `F` in the `become` message? Source, bytecode, function name, etc? ie. does the universal server need F in its "classpath" (or whatever the Erlang equivalent is)? If not, are there platform &| security issues when calling the universal server distributed? Genuine question. It's a fantastic attribute of Erlang to be able to do this.
- eru 3y agoThere are no extra security issues, because at this point you already assume that the other side is trustworthy. I think you can assume that they are sending something like bytecode, but might optimize that, if both are eg on the same physical machine.
- dottedmag 3y agoIt will be a closure "fun () -> ...", and it's serialized bytecode + a copy of captured variables — remember, they all are immutable. For a intra-node call it will be a reference to the bytecode, no need to copy anything. However if this code refers to any functions from modules via syntax "foo:bar/1" then these references will be resolved on the target node, and both nodes better have the same versions of modules loaded.
- s6ro 3y agoA short (35 min) overview of BEAM and why it is not like other VMs: JVM, Node https://www.youtube.com/watch?v=pO4_Wlq8JeI https://www.youtube.com/watch?v=pO4_Wlq8JeI
- bmitc 3y agoI have watched his similar talk The Soul of Erlang and Elixir, and it was wonderful. It gets me excited everytime.
- kaycey2022 3y agoDo we have any alternative that compares to planet labs today?
- ak_111 3y agoQuestion: I am considering learning Go or Elixir to develop the backend for a high-frequency financial application. Focused strictly on concurrency and scalability benefits of each, which would be the better choice? Granted they are probably both great at highly parallel and distributed computing applications, I am just wondering if there is an interesting differentiation between these two in this aspect that I should be aware of.
- bmitc 3y agoWithout more details, you're just going to get people's favorite language recommended or about how neither are suited to high-frequency trading applications.
- ak_111 3y agoMy question is more general: what are some relative pros/cons of Go vs Erlang/Elixir when it comes to their approach to concurrency?
- bmitc 3y agoI don't really know about Go. I favor functional languages, and so I am drawn to Erlang and Elixir. I recommend watching Sasa Juric's talks on YouTube to best understand Erlang and Elixir's concurrency features. One of his talks is posted elsewhere in this thread already.
- ramchip 3y agoI replied more in details elsewhere in the comments, but in a nutshell Erlang/Elixir processes are a tool to contain errors, and they make some opinionated design choices to achieve that. Goroutines are a pure concurrency primitive, they don't contain errors. A goroutine crashing will take down the entire program. Elixir would be absolutely beautiful for a trading program - you could run each strategy (trading algorithm instance) in a separate process, so that a bug in one of them can't take down the whole system, and use monitors to guarantee orders are cancelled if a strategy crashes. I actually started learning it because I worked on such a system, and it was hard to sleep at night when any small coding error in the giant C++ codebase could send the whole thing crashing. It would definitely be too slow for HFT though :)
- coldtea 3y agoDon't know Erlang aside from high level understanding about the language (basically know what one who read about it, but never programmed in it, would). Why is this needed (or, at least why is it nice to have)?: universal_server() -> receive {become, F} -> F() end. I mean what purpose does it serve other than having F() directly? One could just directly spawn F on the remote machines, no? (Perhaps that would need to have the code for F already on those remote vms, whereas this eg. also serializes and forwards F's code?) I would understand the utility if this also handled some common boilerplate, but it seems to just wait for become F message and then doing F().
- rdtsc 3y agoThis code while cool is not very idiomatic or perhaps not something you’d see in production often. That’s ok, Joe loved to explore and experiment. The way it works is that F is a closure. It contains some code but could have captured variables as well as it’s environment. Calling F() then executes the code and it will have access to the whole surrounding environment when F was created. In Erlang we can send a closure across the network to another Erlang node. That can be used and is cool but you still need the module code referenced in the closure available on all nodes. Long story short, in production code you’re right, we would just call the F module directly and pass its the arguments explicitly. This is more of a cool demonstration of possibilities.
- IggleSniggle 3y agoDoesn't this also allow hot-reloading on code update? You can update the code, and upon receiving, auto-reload it. So that "is just a closure" is adding a LOT of context (ie the entire codebase).
- toast0 3y agoYou could use this for hotloading: send every (relevant) process a message with a new closure to run, and they begin running it when they process that message, but BEAM provides other infrastructure for hotloading. For modules, BEAM can have two versions loaded: the current version, and an old version. When you make a fully qualified function call module:function(...), it will call the current version, but calls within the module stay within the same version. So if you load a module's new beam file, all processes that make fully qualified calls into that module get the new version. In case you're wondering, if you load a third version, any processes that still running in the first version get killed (you can check before you load a third version if you don't want that). It's idiomatic to write receive loops as loop(State) -> NewState = receive ... end, ?MODULE:loop(NewState). so that if a new version of the module is loaded, processes will update after they next receive a message. Then you might add a timeout or a heartbeat message to ensure the processes update in a reasonable timeframe. Or use gen_server which has the loop and calls your module so processes only linger in your old module while working on a request.
- rpxio 3y agoThis blog post was recently mentioned in this talk, which is excellent: https://youtu.be/pQ0CvjAJXz4 https://youtu.be/pQ0CvjAJXz4
- rramadass 3y agoExcellent Presentation! Covers/Explains all the steps in a High-Availability, Fault-Tolerant, Distributed Erlang-based system architecture beautifully. Thank you very much for the pointer.
- whalesalad 3y agoIs this the system where every patient is represented by an erlang process?
- NoMoreNicksLeft 3y agoGet a load of this guy... he knows two erlang programs.
- corroclaro 3y agoI miss Joe. His infectious enthusiasm for doing computing _better_ left such an imprint on young me and the way I approach technology today.
- davidw 3y agoI miss working with Erlang and BEAM. It's a well built system and in some ways quite different from other things out there.