我有此代码:
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。
答案 0 :(得分:2)
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: ...
答案 1 :(得分:0)
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"]),
{false, false} <- {is_nil(m1), 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
...
答案 2 :(得分:0)
我发现自己仅在特定情况下使用import React from "react";
import Autocomplete from "@material-ui/lab/Autocomplete";
export default function ComboBox() {
return (
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={option => option.title}
style={{ width: 300 }}
renderInput={params => (
<div ref={params.InputProps.ref}>
<label {...params.InputLabelProps}>My Label </label>
<input {...params.inputProps} autoFocus />
</div>
)}
/>
);
}
,在那些情况下它确实有帮助,主要是在一些类似于“管道”的操作集合中,例如,您需要下一个步骤的结果一个,但实际上是异构的,您没有或没有必要创建一些令牌结构来保存转换和错误(类似于ecto变更集)。
在这些情况下,如果需要知道失败的步骤,我会发现将with
语句包装在带标签的元组中有帮助,因为这样您就可以匹配失败的特定标签。除此之外,您并没有真正使用惯用代码,因为您使用with
只是一个赋值表达式,如果您使用模式匹配,那么它会变得更具可读性,并且在我看来也更加惯用。对于您的示例,这意味着:
with
模式与您想要的结构完全匹配,并且知道Repo.get将返回架构struct或nil,所以它使您不必检查它是否为nil,如果不是架构结构,它将保持为零(除非您将Repo.get与带有返回其他内容的select子句的selectc一起使用)。
请记住,访问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
可能会返回params["some_key"]
,并且在尝试执行nil
时会抛出异常,因此您可以再添加两个带有id条件的语句,并启用如果未找到ID,则返回ID(假设ID是数字ID)(如果是二进制,请将Repo.get
更改为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
语句,因为它只有2个“案例” :
case