我有这样的代码:
def edit(conn, params) do
with m1 <- Repo.get(Model1, params["model1_id"]),
m2 <- Repo.get(Model2, params["model2_id"]),
!is_nil(m1) and !is_nil(m2)
do
# 1
res = !is_nil(m1) and !is_nil(m2)
IO.puts("***** res: #{res}") # ===> false
IO.puts("***** m1: #{Kernel.inspect(m1)}") # ===> prints a struct
IO.puts("***** m1 is_nil: #{is_nil(m1)}") # ===> false
IO.puts("***** m2: #{Kernel.inspect(m2)}") # ===> nil
IO.puts("***** m2 is_nil: #{is_nil(m2)}") # ===> true
else
#2
_ -> raise ArgumentError, "not found"
end
end即使m2为零,流#1也会被执行。怎么可能呢?怎么修呢?目标-确保m1和m2不是零,然后执行流#1。
发布于 2020-05-30 07:10:43
The with
with表达式严格用于模式匹配。它不是if-else条件词的“链式替代”。
基本上,with将遍历所有子句,并尝试将它们与<-箭头的左侧匹配。当第一个模式匹配失败(不匹配)时,它将只执行一个错误子句。
代码的问题
with中的第三行是!is_nil(m1) and !is_nil(m2),它总是成功地匹配模式,即使表达式本身等于false。
修复
要使代码执行您实际需要的操作,您应该在第三行中添加一个左侧,以便它被迫进行模式匹配:
with m1 <- Repo.get(Model1, params["model1_id"]),
m2 <- Repo.get(Model2, params["model2_id"]),
{false, false} <- {is_nil(m1), is_nil(m2)} do
...惯用药
为了使代码更具有习惯性,您还可以使用守卫,is_nil是允许的。这将使您的代码看起来像:
with m1 when not is_nil(m1) <- Repo.get(Model1, params["model1_id"]),
m2 when not is_nil(m2) <- Repo.get(Model2, params["model2_id"]) do
...可读性更好
最后一条建议是始终关注可读性。您正在编写供人阅读的代码,因此较少发生在行上的事情通常更容易阅读。
您的代码将更具可读性,如下所示:
m1 = Repo.get(Model1, params["model1_id"])
m2 = Repo.get(Model2, params["model2_id"])
with m1 when not is_nil(m1) <- m1,
m2 when not is_nil(m2) <- m2 do
...你真的需要一个with吗?
您的with除了确保m1和m2不是nil之外,什么也不做。使用case或if也可以很容易地做到这一点,因为这里实际上不需要任何模式匹配:
m1 = Repo.get(Model1, params["model1_id"])
m2 = Repo.get(Model2, params["model2_id"])
if !is_nil(m1) && !is_nil(m2) do
...发布于 2020-05-30 05:51:16
如果并且只有子句中没有匹配时,Kernel.SpecialtForms.with/1“早期返回”。
在第三个子句中,您有!is_nil(m1) and !is_nil(m2) (大致意思是_ <- !is_nil(m1) and !is_nil(m2) ),它无论如何都匹配。为了实现您想要的结果,您需要在with中显式地使用适当的<-子句
with m1 <- Repo.get(Model1, params["model1_id"]),
m2 <- Repo.get(Model2, params["model2_id"]),
true <- !is_nil(m1) and !is_nil(m2), do: ...更自然的做法是使用适当的警卫来早期返回错误:
with m1 when not is_nil(m1) <- Repo.get(Model1, params["model1_id"]),
m2 when not is_nil(m2) <- Repo.get(Model2, params["model2_id"]),
do: ...事实上,这里不需要with/1。这将非常完美(感谢nil是falsey):
if Repo.get(Model1, params["model1_id"]) &&
Repo.get(Model2, params["model2_id"]), do: ...发布于 2020-05-31 03:04:06
我发现我只在特定的情况下使用with,而在那些情况下,它确实很有帮助,主要是当它是一组类似于“管道”的操作时,就像在下一个步骤中一样,您需要前面步骤的结果,但实际上是异构的,您没有或者没有意义创建一些令牌结构来保存转换和错误(类似ecto )。
在这些情况下,如果知道需要执行失败的步骤,我发现在标记的元组上包装with语句会有所帮助,因为这样就可以匹配已失败的特定标记。此外,您并没有真正使用惯用代码,因为您使用的是with作为赋值表达式,如果使用模式匹配,那么它将变得更加可读性,在我看来,它更具有惯用性。就你的例子而言,这意味着:
with {_, %Model1{} = m1} <- {Model1, Repo.get(Model1, params["model1_id"])},
{_, %Model2{} = m2} <- {Model2, Repo.get(Model2, params["model2_id"])}
do
# we have both m1 and m2 and they are respectively instances of Model1 and Model2
# do something with them
{:ok, {m1, m2}}
else
{Model1, _} ->
#failed fetching Model1
{:error, :no_model1}
{Model2, _} ->
#failed fetching Model2
{:error, :no_model2}
end对所需结构进行模式匹配,并且知道Repo.get将返回模式结构或零,因此不必检查它是否为零,如果不是模式结构,则为零(除非使用带有select子句的Repo.get来返回其他ofc)。
请记住,访问params["some_key"]可能会返回nil,这将在尝试执行Repo.get时引发异常,因此您可以添加两个带有id条件的语句,并且还可以返回id的条件,前提是id是数字id(如果二进制文件将is_integer更改为is_binary):
with {_, id1} when is_integer(id1) <- {:id1, Map.get(params, "model1_id")},
{_, id2} when is_integer(id2) <- {:id2, Map.get(params, "model2_id")},
{_, _, %Model1{} = m1} <- {Model1, id1, Repo.get(Model1, id1)},
{_, _, %Model2{} = m2} <- {Model2, id2, Repo.get(Model2, id2)}
do
# we have both m1 and m2 and they are respectively instances of Model1 and Model2
# do something with them
{:ok, {m1, m2}}
else
{id_type, id_value} when id_type in [:id1, :id2] ->
# one of the id params wasn't an integer
{:error, {:unexpected_id, id_type, id_value}}
{Model1, id, _} ->
# failed fetching Model1
{:error, {:no_model1, id}}
{Model2, id, _} ->
# failed fetching Model2
{:error, {:no_model2, id}}
end可能需要更好地处理这些参数,比如在执行到此阶段之前对它们进行验证,如果这样做了,您可能会使用case语句,因为它只有2种“情况”:
case Repo.get(Model1, valid_id_1) do
%Model1{} = model1 ->
case Repo.get(Model2, valid_id_2) do
%Model2{} = model2 -> {:ok, {model1, model2}}
nil -> {:error, {:no_model2, valid_id_2}}
end
nil -> {:error, {:no_model1, valid_id_1}}
endhttps://stackoverflow.com/questions/62098298
复制相似问题