
在上一篇博客《漂亮的继续with,鱼与熊掌可以兼得》中,探索展现了with的语句优雅之处,然而在比较with与|>时,继续言犹未尽,探索讲得不够透彻。语句
在那篇博客中,继续我说:
毕竟with/1并不是探索try/catch,它并不能捕获执行中抛出的语句错误,然后转向else进行错误处理。继续只有当模式匹配出现错误时,探索才会转向else。语句 要优雅地处理错误,继续并用优雅的探索with/1将逻辑串联起来,就需要重构get_user,语句get_response,send_response等函数。服务器租用当程序逻辑正确时,返回一个tuple对象{ :ok, result};如果出现错误,则返回{ :error, error}。 如果进行了这样的重构,是否意味着|>也可以将健壮性与优雅结合起来呢?因为在Elixir中,函数的定义使用了模式匹配,因此,在定义参与|>操作的函数时,可以通过模式匹配来考虑各种情况,这其中可以包含对{ :error, error}情形的处理,使得数据流不至于在流经该函数时因为错误而崩溃掉。
Joseph Kain在博客Learning Elixirs with给出了一个例子,执行了ecto查询:
defp results(conn, search_params) do conn.assigns.current_user |> Role.scope(can_view: Service) |> within(search_params) |> all |> preload(:user) end defp within(query, %{ "distance" => ""}), do: { :ok, query} defp within(query, %{ "distance" => x, "location" => l} do { dist, _} = Float.parse(x) Service.within(query, dist, :miles, l) end defp within(query, _), do: { :ok, query} defp all({ :error, _} = result), do: result defp all({ :ok, query}), do: { :ok, Repo.all(query)} defp preload({ :error, _} = result), do: result defp preload({ :ok, enum}, field) do { :ok, Repo.preload(enum, field)} end 且不管业务,我们可以清晰地看到在all与preload函数增加了对{ :error, _}分支的处理,这样就可以避免数据流动的网站模板管道不至于因为错误而终止。
如果使用with,虽然结构不如|>清晰直观,却可以避免在all与preload中去处理错误分支。因为with语句同样使用了模式匹配,只要参与的方法不能满足模式匹配的条件,就不会再执行do,从而规避了错误引起的终止:
defp results(conn, search_params) do with user <- conn.assigns.current_user, query <- Role.scope(user, can_view: Service), { :ok, query} <- within(query, search_params), query <- all(query), do: { :ok, preload(query, :user)} end defp within(query, %{ "distance" => ""}), do: { :ok, query} defp within(query, %{ "distance" => x, "location" => l} do { dist, _} = Float.parse(x) Service.within(query, dist, :miles, l) end defp within(query, _), do: { :ok, query} defp all(query), do: Repo.all(query) defp preload(enum, field) do: { :ok, Repo.preload(enum, field)} 由于all/1与preload/2仅仅是对Repo.all/1与Repo.preload/2的简单封装,所以可以进一步简化代码:
defp results(conn, search_params) do with user <- conn.assigns.current_user, query <- Role.scope(user, can_view: Service), { :ok, query} <- within(query, search_params), query <- Repo.all(query), do: { :ok, Repo.preload(query, :user)} end 多余的代码被有效地清除了,而功能与健壮性并没有得到任何降低。这是within的奇妙之处。
【本文为专栏作者“张逸”原创稿件,转载请联系原作者】
戳这里,源码库看该作者更多好文