> For the complete documentation index, see [llms.txt](https://elixir.petrolidas.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://elixir.petrolidas.com/exhort/day-21.md).

# Day 21

## Exercises

* [x] [Top Secret](#top-secret)

### Top Secret

[Exercise on Exercism](https://exercism.org/tracks/elixir/exercises/top-secret) **|** [View my solution](https://exercism.org/tracks/elixir/exercises/top-secret/solutions/petros)

#### Solution

<details>

<summary>Expand to see code (spoiler alert)</summary>

{% code lineNumbers="true" %}

```elixir
defmodule TopSecret do
  @spec to_ast(String.t()) :: tuple()
  def to_ast(string), do: Code.string_to_quoted!(string)

  @spec decode_secret_message_part(tuple(), list()) :: tuple()
  def decode_secret_message_part(ast, acc) when elem(ast, 0) in [:def, :defp] do
    ast
    |> elem(2)
    |> Enum.at(0)
    |> get_function_ast()
    |> get_function_name_and_arity()
    |> get_sliced_name()
    |> get_secret_message_part(ast, acc)
  end

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

  defp get_secret_message_part(name, ast, acc), do: {ast, [name | acc]}

  defp get_function_ast(ast) when elem(ast, 0) == :when do
    ast
    |> elem(2)
    |> Enum.at(0)
  end

  defp get_function_ast(ast), do: ast

  defp get_function_name_and_arity(function_ast) do
    fname =
      function_ast
      |> elem(0)
      |> Atom.to_string()

    {fname, get_arity(function_ast)}
  end

  defp get_arity(function_ast) when elem(function_ast, 2) == nil, do: -1

  defp get_arity(function_ast) do
    function_ast
    |> elem(2)
    |> Enum.count()
    |> Kernel.-(1)
  end

  defp get_sliced_name({_fname, arity}) when arity == -1, do: ""
  defp get_sliced_name({fname, arity}) when arity > -1, do: fname |> String.slice(0..arity)

  @spec decode_secret_message(String.t()) :: String.t()
  def decode_secret_message(string) do
    string
    |> to_ast()
    |> Macro.postwalk([], &decode_secret_message_part/2)
    |> Tuple.to_list()
    |> Enum.at(1)
    |> Enum.reverse()
    |> Enum.join()
  end
end

```

{% endcode %}

[View gist on GitHub](https://gist.github.com/petros/8c6b0effcd78d9e1b3f1c9f4da7299b9)

</details>

#### Notes

I struggled with this one a lot. My first version of the code was not idiomatic at all. It is too verbose and relies a lot on `Enum.at` and `elem`. That's in comparison to using pattern matching.

In my second iteration I've borrowed some improvements from others. Namely, checking if the expression is a function with `a in [:def, :defp]` which I was checking using a conditional statement before.

But I still decided to keep my code instead of using pattern matching. Because of the complexity of the AST data structure, the pattern matching is rather complicated and I feel it's not as readable. Despite it being shorter. But this is really just a preference. Most consider the solutions that rely on pattern matching heavily more elegant.

I have also learned about [mix\_test\_watch | Hex](https://hex.pm/packages/mix_test_watch) and I have started using it. It helps me run all tests on every save. I install the dependency:

```elixir
# mix.exs (v1.13)
def deps do
  [
    {:mix_test_watch, "~> 1.0", only: :dev}
  ]
end
```

I configure it in my project:

```elixir
# config/config.exs
import Config

if config_env() == :dev do
  config :mix_test_watch,
    clear: true
end
```

Then I start the watcher in iTerm:

```shell
mix test.watch --seed 0 --max-failures 1 --include pending
```

I have VSCode and iTerm side by side and I work on my code. On every save, the tests run in iTerm. I have reduced using `iex` dramatically as I am adding `IO.inspect` statements that immediatelly show up in iTerm after each save.

## Overall progress

<figure><img src="/files/BzKhQMGn50jqoeNwWTXm" alt="An image showing my progress on Exercism. It&#x27;s 22.4% as of Sunday, 28 August 2022."><figcaption><p>Progress</p></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://elixir.petrolidas.com/exhort/day-21.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
