Why can't I inspect variable in function when it is in scope?

In the below function, I’m trying to see what value is being passed in el but for some iex throws an error (see below).

list
|> Enum.reduce([], fn el, acc ->
      [[el] | acc]
      |> IO.inspect()
      |> List.flatten() # output: ["Y", "L", "B", "A", "R", "T", "S", "N", "O", "M", "E", "D"]
      |> IO.inspect(el)
    end)

ERROR:

iex(19)> Isogram.isogram?("DEMONSTRABLY")
** (FunctionClauseError) no function clause matching in IO.inspect/3    
    
    The following arguments were given to IO.inspect/3:
    
        # 1
        :stdio
    
        # 2
        ["D"]
    
        # 3
        "D"
    
    Attempted function clauses (showing 1 out of 1):
    
        def inspect(device, item, opts) when is_list(opts)
    
    (elixir) lib/io.ex:399: IO.inspect/3
    (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3

Because IO.inspect/3 expects a keywordlist of options where you passed it el, which seems to be a string.

Probably you wanted to do this instead?

result = [[el] | acc]
  |> IO.inspect()
  |> List.flatten() # output: ["Y", "L", "B", "A", "R", "T", "S", "N", "O", "M", "E", "D"]

IO.inspect(el)

result

When I do that, I get this. What does the error mean?

"D"
** (FunctionClauseError) no function clause matching in :lists.do_flatten/2    
    
    The following arguments were given to :lists.do_flatten/2:
    
        # 1
        "D"
    
        # 2
        []

That you somewhere do not have a list, where a list is expected.

Can you show more of the code and also show the input?

This is the code:

defmodule Isogram do
  @doc """
  Determines if a word or sentence is an isogram
  """
  @spec isogram?(String.t()) :: boolean
  def isogram?(sentence) do
    sentence
    |> String.upcase()
    # turn into list
    |> String.split()
    |> Enum.map(fn x -> String.graphemes(x) end)
    |> List.flatten()
    |> Enum.reduce([], fn el, acc ->
      [[el] | acc]
      |> List.flatten()
    end)
  end
end

Sample input and result:
Isogram.isogram?("DEMONSTRABLY")
["Y", "L", "B", "A", "R", "T", "S", "N", "O", "M", "E", "D"]

Took off the problematic part where I inspect each element in the list so it wouldn’t throw error.

acc will already be a flattened list, when you get into the second iteration.

And you can not flatten down [["E"], "D"]. What you actually want is properly building your accumulator by not wrapping el into a list.

|> Enum.reduce([], fn el, acc -> [el | acc] end) should totally do. (and could even be written as |> Enum.reduce([], &[&1 | &2]))

I’m still unable to do IO.inspect(el) at the end. The idea is that within the reduce function, I would check if the element currently being passed in, is in the accumulator already. If yes, return false. If not, continue.

:wave:

You might find Enum.reduce_while/3 useful.

defmodule Isogram do
  @doc """
  Determines if a word or sentence is an isogram
  """
  @spec isogram?(String.t()) :: boolean
  def isogram?(sentence) do
    sentence
    |> String.upcase()
    # turn into list
    |> String.split()
    |> Enum.map(fn x -> String.graphemes(x) end)
    |> List.flatten()
    |> Enum.reduce_while([], fn el, acc ->
      if el in acc do
        {:halt, false}
      else
        {:cont, [el | acc]}
      end
    end)
    |> case do
      false -> false
      _other -> true
    end
  end
end
iex(15)> Isogram.isogram?("hey you")
false
iex(16)> Isogram.isogram?("isogram")
true
2 Likes