** (CompileError) undefined function <-/2

Based on Erlang Port communication usage:

defmodule Sandbox do

port = Port.open({:spawn,'python3 -u add_server.py'},[:binary|[packet: 4]])
div = fn(a,b)->
  port <- {self,{:command,term_to_binary({a,b})}}
  receive do {_,{:data,b}} -> binary_to_term(b) after 100->{:error,:timeout} end
end
IO.puts "send {a,b}={32,10}, python result : #{inspect div.(32,10)}"
IO.puts "send {a,b}={2,0}, python result : #{inspect div.(2,0)}"
IO.puts "send {a,b}={1,1}, python result : #{inspect div.(1,1)}"

end

Error:

$ iex -S mix
Erlang/OTP 23 [erts-11.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Compiling 1 file (.ex)

== Compilation error in file lib/Sandbox.ex ==
** (CompileError) lib/Sandbox.ex:5: undefined function <-/2
    (elixir 1.10.3) src/elixir_fn.erl:15: anonymous fn/4 in :elixir_fn.expand/3
    (stdlib 3.13.2) lists.erl:1358: :lists.mapfoldl/3
    (elixir 1.10.3) src/elixir_fn.erl:20: :elixir_fn.expand/3

How should I proceed?

It seems like the Elixir readme is not updated since pre-1.0. You should use send/2 instead:

port = Port.open({:spawn,'python3 -u add_server.py'}, binary: true, packet: 4)
div = fn(a,b)->
  send(port, {self(),{:command,term_to_binary({a,b})}})
  receive do {_,{:data,b}} -> binary_to_term(b) after 100->{:error,:timeout} end
end
IO.puts "send {a,b}={32,10}, python result : #{inspect div.(32,10)}"
IO.puts "send {a,b}={2,0}, python result : #{inspect div.(2,0)}"
IO.puts "send {a,b}={1,1}, python result : #{inspect div.(1,1)}"
1 Like

Thanks. It also looks like term_to_binary and binary_to_term should be :erlang.term_to_binary and :erlang.binary_to_term but please correct me if I’m mistaken. (See below)

defmodule Sandbox do

port = Port.open({:spawn,'python3 -u add_server.py'}, binary: true, packet: 4)
div = fn(a,b)->
  send(port, {self(),{:command, :erlang.term_to_binary({a,b})}})
  receive do {_,{:data,b}} -> :erlang.binary_to_term(b) after 100->{:error,:timeout} end
end
IO.puts "send {a,b}={32,10}, python result : #{inspect div.(32,10)}"
IO.puts "send {a,b}={2,0}, python result : #{inspect div.(2,0)}"
IO.puts "send {a,b}={1,1}, python result : #{inspect div.(1,1)}"

end
== Compilation error in file lib/Sandbox.ex ==
** (ArgumentError) argument error
    :erlang.open_port({:spawn, 'python3 -u add_server.py'}, [binary: true, packet: 4])
    lib/Sandbox.ex:3: (module)
    (stdlib 3.13.2) erl_eval.erl:680: :erl_eval.do_apply/6

What the issue here?

As error says you are passing bad argument … The issue is in 2nd argument.

Here goes an erlang specification:

open_port(PortName, PortSettings) -> port()
	
Types
PortName =
    # …
PortSettings = [Opt]
Opt =
    {packet, N :: 1 | 2 | 4} |
    # …
    in | out | binary | eof |
    # …

Look that binary is not defined in same way as {packet, N :: 1 | 2 | 4} is. Passing [packet: 4] is of course ok since Keyword is a List of Tuple i.e. [packet: 4] == [{:packet, 4}]. However the specification of binary is different. It’s not in Tuple format which means we can’t make it {:binary, true} or with Elixir’s sugar: [binary: true].

The correct call is:

:erlang.open_port({:spawn, 'python3 -u add_server.py'}, [:binary, packet: 4])

The :binary part may be confusing for you. In many places Atom is used as a flag, so when checking arguments we are looking not if binary option is set to true, but if :binary flag is a member of List.

2 Likes

Thanks! Hopefully they’ll update their readme soon.