0

I'm completely new to Elixir but had to use Erlang for a piece of coursework and I am currently trying to line by line translate it to Elixir.

I have it compiling successfully and everything seems good to go. I want to start a server process using the start_server() function, which spawns a server on a new process and then send it a message from iex:

  def server(s, us, sums, n) do
    IO.puts("Server online")
    receive do
      :finished ->
        :ok
        %... More message patterns 
        end
    end
  end

  def start_server() do
    server_pid = spawn(server(0, 0, [], 0))
    Process.register(server_pid,:server)
    IO.puts("server started and registered. Should be waiting for message")
  end

However, when running start_server in iex, it seems to be stuck waiting for a message, not allowing me to send any more commmands, unlike what I'm used to in Eshell. I looked online for simple examples of this same scenario (spawning a process that is waiting for messages) and found this "Salutator" example, but following it's instructions lead to the same issue. Is there something simple that I am missing?

DiDi
  • 3
  • 1
  • In erlang there are three versions of spawn:`spawn/1` and `spawn/2` take a function as an argument, and `spawn/3` takes the name of a function as one of its arguments. Next, a function and the return value of a function are different things: if you write `fun(x,y) -> x+y end` that's an anonymous function, and `fun get_stuff/3` is how you refer to a named function. On the other hand, when you write `my_fun(1, 2)`, you must LEARN that all function calls in your code are replaced by their return value. So, unless `server(0, 0, [], 0)` returns a function, that is an error.... – 7stud Jun 12 '23 at 01:05
  • ...because `spawn/1` requires a function as an argument--not some other thing returned by calling a function. Another way of putting it is: all function arguments must be evaluated before they can be passed to the function. Function arguments can be simple expressions like `my_func(X+Y, Z*2)` or they can be the return value of a function call: `my_func(other_func(), Z*2)`. You have made the mistake of thinking that calling a function is the same thing as specifying a function as an argument. Anonymous function in elixir: `fn (a, b) -> a + b end`, reference to named function: `&name/arity` – 7stud Jun 12 '23 at 01:23

1 Answers1

1

When using spawn, you need to pass it a function to run, like one of these two:

spawn(fn -> IO.puts("hi from pid #{inspect(self())}")
spawn(&MyModule.print_my_pid/0)

What you're doing is the equivalent of this:

IO.inspect(self())
spawn(IO.inspect(self()))
# This code prints:
#PID<0.108.0>
#PID<0.108.0>
** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not a fun

    :erlang.spawn(#PID<0.108.0>)
    iex:6: (file)

As you can see, both the self() calls had the same PID - they both ran on the parent process. Then, spawn was called with the output of that, which is not a function and thus invalid.

What you want here is probably to wrap the server call in an anonymous function:

server_pid = spawn(fn -> server(0, 0, [], 0) end)
Leonardo Dagnino
  • 2,914
  • 7
  • 28
  • Thank you! I was thinking the way I was doing it as passing the function and not the return value, but this definitely makes sense to me now. Before seeing this answer I got around it by using spawn/3, but this seems much more appropriate. – DiDi Jun 16 '23 at 12:09