继续探索with语句

在上一篇博客《漂亮的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的奇妙之处。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2017-04-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

6878
来自专栏张善友的专栏

Mix 10 上的asp.net mvc 2的相关Session

Beyond File | New Company: From Cheesy Sample to Social Platform Scott Hansel...

2587
来自专栏跟着阿笨一起玩NET

c#实现打印功能

2792
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

31810
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2182
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

4928
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4035
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

2978
来自专栏java 成神之路

使用 NIO 实现 echo 服务器

4667
来自专栏张善友的专栏

LINQ via C# 系列文章

LINQ via C# Recently I am giving a series of talk on LINQ. the name “LINQ via C...

2645

扫码关注云+社区