我有此代码:
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。
with
with
表达式严格用于模式匹配。它不是if-else
条件的“可链接替代”。
基本上with
将遍历您的所有子句,并尝试将其与<-
箭头的左侧进行模式匹配。当第一个模式匹配失败(不匹配)时,它将仅执行error子句中的一个。
with
中的第三行是!is_nil(m1) and !is_nil(m2)
,即使表达式本身等于false
,也总是会成功进行模式匹配。
要使代码执行您实际想要的操作,应在第三行添加左侧,以便强制其进行模式匹配:
with m1 <- Repo.get(Model1, params["model1_id"]), m2 <- Repo.get(Model2, params["model2_id"]), {true, true} <- is_nil(m1) and is_nil(m2) do ...
惯用药剂
[为了使代码更加通用,您还可以使用Guards,允许使用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
...
[Kernel.SpecialtForms.with/1
“仅当该子句中没有匹配项时才返回”。
[我发现自己仅在特定情况下使用if Repo.get(Model1, params["model1_id"]) &&
Repo.get(Model2, params["model2_id"]), do: ...
,在这些情况下它确实有帮助,主要是在一些类似于“管道”的操作集合中,例如,您需要下一个步骤的前一个结果,但是确实是异构的,您没有或没有必要创建一些令牌结构来保存转换和错误(类似于ecto变更集)。