Question

The following code is supposed to read a file and create an Item record for each line:

defmodule Ship do
  defrecord Item, product_code: 0, quantity: 0, destination: ""

  def load_data do
    File.read!("data") 
      |> String.split 
      |> Enum.map &(String.split &1, ",")
      |> Enum.map &(list_to_item &1)
  end

defp list_to_item([pc, q, d | []]) do
    {parsed_q, _} = Integer.parse q
    Item.new product_code: pc, quantity: parsed_q, destination: d
  end
end

There is a data file with the following content:

1,100,London
1,30,Lisbon
3,2,Braga

The problem is, when I execute the load_data function, it looks like the last call to Enum.map is trying to call the parent function (load_data) recursively, and throwing a BadArityError.

The output is as follows:

iex(1)> Ship.load_data
** (BadArityError) #Function<0.127338698/1 in Ship.load_data/0> with arity 1 called with 2 arguments ({:cont, []}, #Function<30.103209896/2 in Enum.map/2>)
    (elixir) lib/enum.ex:2000: Enumerable.Function.reduce/3
    (elixir) lib/enum.ex:879: Enum.map/2
            ship.ex:8: Ship.load_data/0

The interesting part is that if I change the load_data function to:

def load_data do
  list = File.read!("data") 
    |> String.split 
    |> Enum.map &(String.split &1, ",")

  Enum.map list, &(list_to_item &1)
end

It works as expected:

iex(2)> Ship.load_data
[Ship.Item[product_code: "1", quantity: 100, destination: "London"],
 Ship.Item[product_code: "1", quantity: 30, destination: "Lisbon"],
 Ship.Item[product_code: "3", quantity: 2, destination: "Braga"]]

So, any idea on where is the error is coming from in the first version? Aren't they supposed to be equivalent?

Btw, I'm using Elixir 0.12.4.

Était-ce utile?

La solution

Your code is actually equivalent to this:

File.read!("data") 
|> String.split 
|> Enum.map (&(String.split &1, ",") |> Enum.map &(list_to_item &1))

You need more parentheses, because |> has a higher precedence than parenthesisless function application:

File.read!("data") 
|> String.split 
|> Enum.map(&(String.split &1, ","))
|> Enum.map(&(list_to_item &1))

This is an unfortunate flaw in the design of Elixir (and the sole reason I still prefer Erlang).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top