1. 44
  1. 14

    great talk just one note

    Because, barring magic, you cannot send a function.

    This is trivial in erlang, and even between nodes in a cluster, and commonly used, not some obscure language feature. So there we go I officially declare Erlang as Dark Magic.

    1. 4

      I suppose it’s not that “you cannot send a function”, but more like “you cannot send a closure, if the language allows references which may not resolve from other machines”. Common examples are mutable data (if we want both parties to see all edits), pointers, file handles, process handles, etc.

      I’m not too familiar with Erlang, but I imagine many of these can be made resolvable if we’re able to forward requests to the originating machine.

      1. 7

        It’s possible to implement this in Haskell, with precise control over how the closure gets de/serialized and what kinds of things can enter it. See the transient package for an example. This task is a great example of the things you can easily implement in a pure language, but very dangerous in impure ones.

      2. 1

        I don’t know Erlang, but… I can speculate that it doesn’t actually send the functions. It sends their bytecode representation. Or a pointer to the relevant address, if the two computers are guaranteed to share code. I mean, the function has to be transformed into a piece of data somehow.

        1. 13

          it doesn’t actually send the functions. It sends their bytecode representation

          How is that different from not actually sending an integer, but sending its representation?

          In Erlang any term can be serialized, including functions, so sending a function to another process/node isn’t different from sending any other term. The nodes don’t need to share code.

          1> term_to_binary(fun (X) -> X + 1 end).
          1. 1

            Would that also work if the function contains free variables?

            That is what’s the result of calling this function:

            fun(Y) -> term_to_binary(fun (X) -> X + Y end) end.

            (Sorry for the pseudo-Erlang)

            1. 5


              1> F1 = fun(Y) -> term_to_binary(fun (X) -> X + Y end) end.
              2> F2 = binary_to_term(F1(2)).
              3> F2(3).

              … or even

              1> SerializedF1 = term_to_binary(fun(Y) -> term_to_binary(fun (X) -> X + Y end) end).
              2> F1 = binary_to_term(SerializedF1).                                           #Fun<erl_eval.7.91303403>
              3> F2 = binary_to_term(F1(2)).                                                  #Fun<erl_eval.7.91303403>
              4> F2(3).                                                                       5

              The format is documented here: http://erlang.org/doc/apps/erts/erl_ext_dist.html

            2. 0

              How is that different from not actually sending an integer

              Okay, it’s not. It’s just much more complicated. Sending an integer? Trivial. Sending a plain old data structure? Easy. Sending a whole graph? Doable. Sending code? Scary.

              Sure, if you have a bytecode compiler and eval, sending functions is a piece of cake. Good luck doing that however without explicit language support. In C for instance.

              1. 6

                You can do it, for instance, by sending a DLL file over a TCP connection and linking it into the application receiving it. It’s harder, it’s flakier, it’s platform-dependent, and it’s the sort of thing anyone sane will look at and say “okay but why though”. It’s just that Erlang is designed to be able to do that sort of thing easily, and accepts the tradeoffs necessary, and C/C++ are not.

                1. 3

                  The Morris Worm of 1988 used a similar method to infect new hosts.

          1. 1

            Here’s the paper by Danvy and Nielsen in case you want to add the link to your blog article.

            1. 1

              This is a great talk. Defunctionalization - I love learning a new word for something