美文网首页Elixir 编程
继续探索with语句

继续探索with语句

作者: _张逸_ | 来源:发表于2017-04-11 11:24 被阅读115次

在上一篇博客《漂亮的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 Elixir's 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 

且不管业务,但我们可以清晰地看到在allpreload函数增加了对{:error, _}分支的处理,这样就可以避免数据流动的管道不至于因为错误而终止。

如果使用with,虽然结构不如|>清晰直观,却可以避免在allpreload中去处理错误分支。因为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/1preload/2仅仅是对Repo.all/1Repo.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的奇妙之处。

相关文章

  • 继续探索with语句

    在上一篇博客《漂亮的with,鱼与熊掌可以兼得》中,展现了with的优雅之处,然而在比较with与|>时,言犹未尽...

  • 继续探索

    丢失了简书,也就没有写过什么,再回来,有种轻舟已过万重山的感觉,也是,过去心不可得,现在心不可得,未来心不可得,安...

  • 继续探索

    太爱自己有点抗拒,当我想放松的时候,我头部位绷得紧紧的追着,一会头脑蹦出一个答案,因为我不够好,不值得被爱,这个是...

  • 继续探索

    没想到时隔半个月,我又开始了和分析师的探索之旅。自从上次告别之后,这两天我时不时还会想念分析师。尤其在我买了...

  • SQL 语句继续

    1,Subquery:SELECT 列名 FROM 表名 WHERE 列名2【比较运算符】( SELECT 列名 ...

  • 1D1S-43: 手绘泰囧趣事

    就酱!继续探索~

  • break语句、continue语句

    1、break语句可用于跳出swtich()语句和循环,在跳出循环后会继续执行循环后的语句。for (var i ...

  • 继续探索吃吃吃

    前面探索绿豆糕已经三次了,味道都是一般般,这次终于下定决心买了脱皮绿豆,黄油若干,重整旗鼓再试一次。 ...

  • 继续探索自主学习

    我越来越觉得自主学习的重要性,觉得通过毛笔小楷抄写文言文的好处。 要继续探索自主学习,就是要先自己动起来,有一个路...

  • 继续探索 砥砺前行

    又到了辞旧迎新的年关,数字货币经历2017的爆发和2018年的煎熬,那么2019又会有什么样的预期? 我个人的总体...

网友评论

本文标题:继续探索with语句

本文链接:https://www.haomeiwen.com/subject/yypyattx.html