defmodule IP do
  defstruct [:host, :port, :name]
  def left_ip == right_ip do
    Kernel.==(left_ip.host, right_ip.host) &&
    Kernel.==(left_ip.port, right_ip.port)
  end
end
In iex:
~/elixir_programs$ iex ip.ex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> left_ip = %IP{host: 10, port: 3, name: "Joe"}   
%IP{host: 10, name: "Joe", port: 3}
iex(2)> right_ip = %IP{host: 10, port: 3, name: "Karen"}
%IP{host: 10, name: "Karen", port: 3}
iex(3)> left_ip == right_ip
false
iex(4)> import Kernel, except: [==: 2]
Kernel
iex(5)> left_ip == right_ip           
** (CompileError) iex:5: undefined function ==/2
iex(5)> import IP                     
IP
iex(6)> left_ip == right_ip
true
Kernel is automatically imported into iex, so you have to import Kernel again excepting the function Kernel.==().
An import statement has to do with namespacing.  By importing IP, you no longer have to precede a function's name with the module name:
IP.==(left, right)
v.
left == right
I wanted structs to be compared as equals when inserted inside a MapSet
No worky with MapSets:
iex(24)> left_ip == right_ip
true
iex(27)> ms = MapSet.new()  
#MapSet<[]>                                                
iex(28)> ms |> MapSet.put("hello") |> MapSet.put(left_ip) |> MapSet.put("hello") |> MapSet.put(right_ip) |> MapSet.put(left_ip) 
#MapSet<[
  %IP{host: 10, name: "Joe", port: 3},
  %IP{host: 10, name: "Karen", port: 3},
  "hello"
]>
Mapset.put calls Map.put which calls :maps.put--which is an erlang function.