How could this be written using continuation passing style? Is there a way to use CPS in this code to make the two insert functions tail-call optimized?

defmodule BTree do

  defstruct tree: {node: nil, left: nil, right: nil}
  def new(element), do: %BTree{tree: {node: element, left: nil, right: nil}}

  #insert when there's some data in the tree
  def insert(%BTree{tree: {node: e, left: l, right: r}}, element) when e &gt; element do
    newl = if l == nil, do: new(element), else: insert(l, element)
    %BTree{tree: {node: e, left: newl, right: r }}
  end

  def insert(%BTree{tree: {node: e, left: l, right: r}}, element) when e &lt; element do
    newr = if r == nil, do: new(element), else: insert(r, element)      
    %BTree{tree: {node: e, left: l, right: newr }} 
  end

  def height(%BTree{tree: {node: _, left: l, right: r}}) do
    lh = if l != nil, do: height(l), else: 0
    rh = if r != nil, do: height(r), else: 0
    1 + max(lh, rh)
  end

end 

#BTree.new(10) |&gt; BTree.insert(5) |&gt; BTree.insert(15) |&gt; BTree.height()


As always any comments on idiomatic Elixir code and style are welcome as well.
I would like to slightly rewrite the problem before making it continuation based because it will make it clearer and avoid duplication:

defmodule BTree do
  defstruct tree: nil
  def new(e), do: %BTree{tree: {e, nil, nil}}

  def insert(%BTree{tree: root}, element) do
    %BTree{tree: do_insert(root, element)}
  end

  defp do_insert(nil, element) do
    {element, nil, nil}
  end

  defp do_insert({e, l, r}, element) when e &gt; element do
    {e, do_insert(l, element), r}
  end

  defp do_insert({e, l, r}, element) when e &lt; element do
    {e, l, do_insert(r, element)}
  end

  defp do_insert({_, _, _} = tuple, _element) do
    tuple
  end
end 

BTree.new(10) |&gt; BTree.insert(5) |&gt; BTree.insert(15) |&gt; IO.inspect


The previous code was generating a BTree for every node. Instead, we simply treat each node as a tuple with three elements (the node item, left and right). When inserting an element, we traverse the tree until we reach nil. We can make it tail recursive by passing a function and invoking the previous one at each step:

defmodule BTree do
  defstruct tree: nil
  def new(e), do: %BTree{tree: {e, nil, nil}}

  def insert(%BTree{tree: root}, element) do
    do_insert(root, element, &amp;%BTree{tree: &amp;1})
  end

  defp do_insert(nil, element, fun) do
    fun.({element, nil, nil})
  end

  defp do_insert({e, l, r}, element, fun) when e &gt; element do
    do_insert(l, element, &amp;fun.({e, &amp;1, r}))
  end

  defp do_insert({e, l, r}, element, fun) when e &lt; element do
    do_insert(r, element, &amp;fun.({e, l, &amp;1}))
  end

  defp do_insert({_, _, _} = tuple, _element, fun) do
    fun.(tuple)
  end
end 

BTree.new(10) |&gt; BTree.insert(5) |&gt; BTree.insert(15) |&gt; IO.inspect


Note though I find this code much harder to understand and I don't think you would get any benefit in practice.