0
votes

I am currently struggle with this requirement. For example, I have a list of functions.

def function_a(a, b, c, d) do
  ...
  function_b(a, b)
  ...
end

defp function_b(a, b) do
  ...
  function_c(a, b)
  ...
end

defp function_c(a, b) do
  ...
  function_d(a, b)
  ...
end

defp function_d(a, b) do
  IO.inspect a
  IO.inspect b
end

Now I want to add IO.inspect d in the function_d. The only way I can think of, is to pass the d as parameter for each function. Which means I need modify function_b, function_c, function_d(In real world, it's more).

In OOP world, we can store the d as an instance variable, and any function in this class can use it directly.

The reason I am asking this question, is that I want to avoid to increase the number of parameters.

Is there any good way in Elixir or Phoenix?

1
Have you tried using a map or a custom struct? e.g. def function_a(my_struct) do would call function_b(my_struct) and defp function_d(my_struct) do would just call IO.inspect(my_struct.d). Please not that structs are not the same as OOP classes, but they might help define the shape of the data in a similar way. - sabiwara
Yeps @sabiwara. Struct could reduce the parameters length by grouping the data in hash. i.e. passing variable like options or data . But here I don't want to use this way because it breaks some meaning of the code. - Stephen

1 Answers

2
votes

Sabiwara's suggestion in the comments is good. If you follow the convention of passing the struct in as the first argument to your functions, and returning the struct from each one, you can write chains of pipes very easily.

defmodule Example do
  defstruct ~w(a b c d)a

  def function_a(%Example{} = example) do
    IO.inspect(example.a, label: "a")
    example
  end

  def function_b(%Example{} = example) do
    IO.inspect(example.b, label: "b")
    example
  end

  def function_c(%Example{} = example) do
    IO.inspect(example.c, label: "c")
    example
  end

  def function_d(%Example{} = example) do
    IO.inspect(example.d, label: "d")
    example
  end

  def call_all() do
    %Example{a: "I'm a", b: "this is b", c: "C too", d: "Finally d"}
    |> function_a()
    |> function_b()
    |> function_c()
    |> function_d()
  end
end

Running in iex:

iex(1)> Example.call_all
a: "I'm a"
b: "this is b"
c: "C too"
d: "Finally d"
%Example{a: "I'm a", b: "this is b", c: "C too", d: "Finally d"}

I don't want to use this way because it breaks some meaning of the code.

Elixir modules do not have state - only functions and arguments. There's no way to access the arguments in one function from another. If you want to avoid a new parameter, you either need to use a data-structure that both functions know about, or store state somewhere (in a process, ETS, the process dictionary...).