:digraph.add_edge
gives unpredictable results when adding an edge, one only knows that the first element in the list will be :"$e"
I am trying to create an atomic :digraph.add_vertex+:digraph.add_edge+:dets.insert function. If any clauses in the with statement below fail, I need to roll back the previous ones. So if the last clause (:dets.insert
) fails, I need to delete the vertex and the edge I just created. In order to do that, I need to destructure the return of the :digraph.add_edge
function in the with
clause, so that I can refer back to the created edge in the else
part…
defp atomic_vedge({detspath, graph}, v, parent, meta, ts) do
# atomic add of a vertex and its edge to parent and save to dets
# if any stage fails preceding successes are rolled back
with {:addvertex, v} <- {:addvertex, :digraph.add_vertex(graph, v, meta)},
{:addedge, [:"$e" | rest]} <- {:addedge, :digraph.add_edge(graph, parent, v)},
{:insert, :ok} <-
{:insert, :dets.insert(detspath, {v, parent, ts, :vertedge, meta})} do
{:ok, {detspath, graph}}
else
{:addvertex, {:error, reason}} ->
{:error, reason}
{:addedge, {:error, reason}} ->
:digraph.del_vertex(graph, v)
{:error, reason}
{:insert, false} ->
:digraph.del_vertex(graph, v)
:digraph.del_edge(graph, [:"$e" | rest])
{:error, "dets vertex insert failed"}
end
end
However this is giving me an error when compiling:
Does this mean I cannot destructure into [first | rest]
in a with
statement? So I cannot track what the :digraph.add_edge
returned in order to roll it back? It seems to be a problem with the [head | rest]
part because the previous {:addvertex, v}
doesn’t seem to cause a problem.
(the problem here btw is that :digraph
is side-effecty. It doesn’t return a new graph each time because it uses ETS tables and that’s why I’m having to jump through these hoops)