Function call expecting list works with a tuple

defmodule Navigator do
  def navigate(dir) do
    expanded_dir = Path.expand(dir)

    go_through([expanded_dir])
  end

  def go_through([]), do: nil

  def go_through([content | rest]) do
    print_and_navigate(content, File.dir?(content))

    go_through(rest)
  end

  def print_and_navigate(_dir, false), do: nil

  def print_and_navigate(dir, true) do
    IO.puts(dir)

    children_dirs = File.ls!(dir)

    go_through(expand_dirs(children_dirs, dir))
  end

  def expand_dirs([], relativeto), do: []

  def expand_dirs([dir | dirs], relative_to) do
    expanded_dir = Path.expand(dir, relative_to)

    [expanded_dir | expand_dirs(dirs, relative_to)]
  end
end

in:

def print_and_navigate(dir, true) do
   IO.puts(dir)

   children_dirs = File.ls!(dir)

   go_through(expand_dirs(children_dirs, dir))
 end

children_dirs is a tuple and expand_dirs expects a list so why do i not get an error? do functions pattern match incoming arguments before throwing errors of arguments compatibility? where can i read more about where and when pattern matching happens?

It doesn’t seem to be. File.ls! returns a list.

1 Like

true… honestly docs are confusing in this section:

ls(path \\ “.”)

@spec ls(Path.t()) :: {:ok, [binary()]} | {:error, posix()}

Returns the list of files in the given directory.

Hidden files are not ignored and the results are not sorted.

Since directories are considered files by the file system, they are also included in the returned value.

Returns {:ok, files} in case of success, {:error, reason} otherwise.

ls!(path \\ “.”)

@spec ls!([Path.t()]) :: [[binary()]]

The same as ls/1 but raises a File.Error exception in case of an error.

What is confusing? You are using the so-called bang function – File.ls! – that does not return tuples; it either returns the values you requested, or raises a runtime error.

You seem to be mistaking it for the non-bang function – File.ls.

Check the @specs more closely.

1 Like

that’s what’s confusing. The docs say it’s the same but with the runtime error the fact that it’s not a tuple isn’t mentioned but only showed in the specs

The docs are saying that it does the same but raises an error instead of returning an error tuple IMO. :person_shrugging: But I can see how it can be confusing if you are new to Elixir.

yeah to a newbie it says: " this one does something more when errors come" when i think it could say: “this one does the same but instead of a it does b when errors come” to be clearer to who hasn’t well in mind elixirs mechanics.

The problem is more that not just behaviour around errors changes, but also around successes.

  • {:ok, result} becomes the unwraped result
  • {:error, error} becomes the error being raised

This is such a common convention it is even documented: Naming conventions — Elixir v1.18.2

But in the docs for this function it’s not really explained well.

2 Likes