我可以使用管道并将结构更新语法吗?

时间:2018-03-15 15:04:22

标签: elixir

我发现我想经常更新结构,然后将结果传递给另一个函数。更新我的结构的需要不断分解我的管道。

我发现自己做了很多这样的事情:

my_struct = %{my_struct | my_field_in_struct: a_new_value} |> my_funct1
my_struct = %{my_struct | my_field_in_struct: a_new_value} |> my_funct2 
my_struct = %{my_struct | my_field_in_struct: a_new_value} |> my_funct3

我想做点什么:

my_struct
|> %{ | my_field_in_struct: a_new_value}
|> my_funct1
|> %{ | my_field_in_struct: a_new_value}
|> my_funct2
|> %{ | my_field_in_struct: a_new_value}
|> my_funct3

原始语法可能不是那么糟糕,但仍然。

我知道我可以使用 Map.put(),但是我必须在我的模块中编写一个函数来将结果映射转换回我的struct类型。

之前有没有人遇到这种微小的烦恼?有没有一个干净的选择?

2 个答案:

答案 0 :(得分:3)

Elixir有什么好处,它有宏。那么,如果这是一个非常常见的应用程序操作,为什么不定义自己的管道操作符呢?

defmodule StructPipe do
  defmacro left ~>> right do
    {:%{}, [], [{:|, [], [left, right]}]}
  end
end

defmodule MyStruct do
  defstruct ~w|foo bar baz|a
end

defmodule StructPipe.Test do
  import StructPipe
  def test do
    %MyStruct{foo: 42}
    ~>> [bar: 3.14]
    ~>> [baz: "FOOBAR"]
  end
end

IO.inspect StructPipe.Test.test, label: "Resulting in"
#⇒ Resulting in: %MyStruct{bar: 3.14, baz: "FOOBAR", foo: 42}

请注意,它可以安全地与普通Kernel.|>/2管道混合使用:

%MyStruct{foo: 42}
|> IO.inspect(label: "Ini")
~>> [bar: 3.14, baz: 3.14]
|> IO.inspect(label: "Mid")
~>> [baz: "FOOBAR"]
|> IO.inspect(label: "Aft")

#⇒ Ini: %MyStruct{bar: nil, baz: nil, foo: 42}
#  Mid: %MyStruct{bar: 3.14, baz: 3.14, foo: 42}
#  Aft: %MyStruct{bar: 3.14, baz: "FOOBAR", foo: 42}

答案 1 :(得分:1)

如果确实想要:

,您也可以传入匿名函数
my_struct
|> (&(%{ &1| my_field_in_struct: a_new_value})).()

my_struct
|> (fn struct -> %{ struct| my_field_in_struct: a_new_value} end).()

但我认为这看起来非常棒/可读