What is a top level function and what isn't it?

Hi community, I hope all of you are good…
I’d like to know why this functions ins’t a top level function?
Is it because the only thing that do is call other private function?

 defmodule Math do
   def sin(x), do: do_sin(x)
   defp do_sin(x), do: nil
 end

More context:
I’m learning about AST, and I had a challenge with understand why this implementation below:
defmodule TopSecret do

def decode_secret_message_part(ast, acc) do
  Macro.prewalk(ast, acc, 
    fn 
      {operation, _, [{name, _, args} | _]} = new_ast, acc)  when operation in [:def,   :defp] ->
        {new_ast, [parse_name_to_str(name, args) | acc]}

      other, acc -> {other, acc}
    end)
  end
end

Because this test always fail using that?!


test "ignores not top-level function definition" do
      string = """
      defmodule Math do
        def sin(x), do: do_sin(x)
        defp do_sin(x), do: nil
      end
      """

      ast = TopSecret.to_ast(string)
      acc = []

      {actual_ast, actual_acc} = TopSecret.decode_secret_message_part(ast, acc)
      assert actual_ast == ast
      assert actual_acc == acc
end

Link for my final solution:

But again, I don’t understand why I’ve success.

Do you have a definition for what is or is not a “top level function” ? This is not a term I’ve heard before. Modules have public function and private functions, that’s it.

With what exact error?

Probably related to the fact that you walk (aka recurse) the AST, which is explicitely not required by the exercise. They ask you to consider just the passed node, and nothing else.

Also, nothing in the concept or exercise description seems to introduce the term “top-level function”, it would be nice to explain what you understand by that term, because as ben said, it is not a term that we use in Elixir, while in other languages I have heard it synonymously used with “prelude”, which means more or less “autoimported functions”, which in this context doesn’t really make sense.

I agree that “top level function” is not something well defined in Elixir.

By my reasoning, defmodule is it in your example.

I don’t know, too.

That’s because I guess it’s weird… For me looks the same thing…
Here the completely challenge:
https://exercism.org/tracks/elixir/exercises/top-secret

Do you know, what is the difference between them below?

def decode_secret_message_part(ast, acc) do
  Macro.prewalk(ast, acc, 
    fn 
      {operation, _, [{name, _, args} | _]} = new_ast, acc)  when operation in [:def,   :defp] ->
        {new_ast, [parse_name_to_str(name, args) | acc]}

      {operation, _, [{:when, _, [{name, _, args} | _]} | _]}  = new_ast, acc)  when operation in [:def,   :defp] ->
        {new_ast, [parse_name_to_str(name, args) | acc]}

      other, acc -> {other, acc}
    end)
  end
end
def decode_secret_message_part(
        {operation, _, [{:when, _, [{name, _, args} | _]} | _]} = ast,
        acc
      )
      when operation in [:def, :defp] do
    {ast, [parse_name_to_str(name, args) | acc]}
  end

  def decode_secret_message_part({operation, _, [{name, _, args} | _]} = ast, acc)
      when operation in [:def, :defp] do
    {ast, [parse_name_to_str(name, args) | acc]}
  end

  def decode_secret_message_part(ast, acc), do: {ast, acc}

  def do_decode(ast) do
    Macro.prewalk(ast, [], &decode_secret_message_part/2)
  end

Why the first fail on the test that I show you and the second success ?

My guess is that this is a leftover name from folks with Ruby experience - you can define methods outside of any explicit class or module in Ruby, which go into the “top-level namespace”.

It’s not a common term in Elixir, since writing def outside of a module just gives an error.

In the specific case of that test, it means the function under test should be looking for def and defp that are NOT nested inside any other structures in the AST.

1 Like

Interesting, but for me it looks inside the module even that it fail.

I don’t know what I’ve couldn’t see!

For example, comparing the result of these functions:

ast1 = Code.string_to_quote!("defmodule Math do
        def sin(x), do: do_sin(x)
        defp do_sin(x), do: nil
      end")

ast2 = Code.string_to_quote("defmodule MyCalendar do
        def busy?(date, time) do
          Date.day_of_week(date) != 7 and
            time.hour in 10..16
        end

        def yesterday?(date) do
          Date.diff(Date.utc_today, date)
        end
      end")

Why ast1 ins’t a top-level func and ast2 is ?

Again “top level functions” are not a thing in Elixir. Assuming al2o3cr is right with their assumption about “top-lvel function” here means “a function not bound to a module”

And the thing you have to check here is if the current node of the AST is a def/defp. Whether or not this node has parents or childrens is none of your concern for the sake of this exercise.

At this point we do not even care whether this node, its (potential) parents or (potential) children make any semantic sense.

We only care whether this node defines a function, and what the name and args of this where.


Neither ast1 nor ast2 are functions. Both hold ASTs of a module definition. Both should fail if passed to your exercises function. as neither node is a def or defp but they are defmodule. The fact that there are def and defp in the “body” is not relevant.

1 Like

Those two ASTs are used to test different functions, so they aren’t directly comparable.

ast1 is used to test decode_secret_message_part:

The intent of decode_secret_message_part is to NOT recurse into nested clauses.

OTOH

ast2 is used to test decode_secret_message:

The intent of decode_secret_message is to recurse into nested clauses, so “top-level” is meaningless here.

My final conclusion is I shouldn’t concern with that at this moment.
I’m grateful to you for your support.

I think that is really hard explain all the context of the my question.
But right it’s enough to know that “top-level function” ins’t a concern in Elixir.
Thank you for your support.