Skip to content

Instantly share code, notes, and snippets.

@TylerPachal
Last active September 26, 2023 15:14
Show Gist options
  • Select an option

  • Save TylerPachal/66e9e7f42d1ff75c9a47c55deb6a7e5d to your computer and use it in GitHub Desktop.

Select an option

Save TylerPachal/66e9e7f42d1ff75c9a47c55deb6a7e5d to your computer and use it in GitHub Desktop.

Revisions

  1. TylerPachal revised this gist Sep 26, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion custom-compile-time-warnings-in-elixir.md
    Original file line number Diff line number Diff line change
    @@ -104,7 +104,7 @@ defmodule Country do
    end
    ```

    Now when we compile the code, we'll get a more helpful preceding the warning from the Elixir compiler:
    Now when we compile the code, we'll get a more helpful warning preceding the warning from the Elixir compiler:

    ```bash
    $ mix compile --force
  2. TylerPachal revised this gist Sep 18, 2023. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions custom-compile-time-warnings-in-elixir.md
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@ When you are writing macros which run at compile-time, it can be useful to emit

    Say you are using a text file to dynamically define function clauses. Our function will be called `official_language/1`; its only argument is the name of a country, and it returns an `:ok` tuple with a list of the countries official languages.

    Foruntaley, for our implementation we have been provided with a `official_languages.csv` file that contains the mappings we need to perform this lookup.
    Fortunately, for our implementation we have been provided with a `official_languages.csv` file that contains the mappings we need to perform this lookup:

    ```text
    Belgium,"Dutch,French,German"
    @@ -19,7 +19,7 @@ Mexico,Spanish
    Italy,Italian
    ```

    Implementing this with metaprogamming would look like this:
    Using the contents of this file to dynamically define our function with metaprogamming would look like this:

    ```elixir
    defmodule Country do
    @@ -74,7 +74,7 @@ warning: this clause cannot match because a previous clause at line 8 always mat

    ### The Solution

    It would be better if we emitted a more helpful error message (after idenitfying non-unique country keys). This can be done using [`IO.warn/1`](https://hexdocs.pm/elixir/1.12.3/IO.html#warn/1).
    It would be better if we emitted a more helpful error message (after identifying non-unique country keys). This can be done using [`IO.warn/1`](https://hexdocs.pm/elixir/1.12.3/IO.html#warn/1):

    ```elixir
    defmodule Country do
    @@ -104,7 +104,7 @@ defmodule Country do
    end
    ```

    Now when we compile the code, we'll get a more helpful preceeding the warning from the Elixir compiler:
    Now when we compile the code, we'll get a more helpful preceding the warning from the Elixir compiler:

    ```bash
    $ mix compile --force
  3. TylerPachal revised this gist Sep 18, 2023. 1 changed file with 16 additions and 12 deletions.
    28 changes: 16 additions & 12 deletions custom-compile-time-warnings-in-elixir.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,17 @@ When you are writing macros which run at compile-time, it can be useful to emit

    ### Example Scenario

    Say you are using a text file to dynamically function clauses. Our function is called `official_language/1` and it takes the name of a country, and returns a list of its official languages (some countries have more than one!). Foruntaley, we have been provided with a `official_languages.csv` file that contains the mappings we need to perform this lookup.
    Say you are using a text file to dynamically define function clauses. Our function will be called `official_language/1`; its only argument is the name of a country, and it returns an `:ok` tuple with a list of the countries official languages.

    Foruntaley, for our implementation we have been provided with a `official_languages.csv` file that contains the mappings we need to perform this lookup.

    ```text
    Belgium,"Dutch,French,German"
    Brazil,Portugese
    Canada,"English,French"
    Mexico,Spanish
    Italy,Italian
    ```

    Implementing this with metaprogamming would look like this:

    @@ -32,17 +42,11 @@ end

    _(Read more about `@external_resource` [here](https://hexdocs.pm/elixir/1.12.3/Module.html#module-external_resource))_

    If you have the following `official_languages.csv` file in your `priv` repo, you'd be able to do the following function calls:

    ```text
    Belgium,"Dutch,French,German"
    Brazil,Portugese
    Canada,"English,French"
    Mexico,Spanish
    Italy,Italian
    ```
    With the CSV file in place, you'd be able to make the following function calls:

    ```bash
    $ iex -S mix

    iex(1)> Country.official_language("Canada")
    {:ok, ["English", "French"]}

    @@ -54,7 +58,7 @@ iex(2)> Country.official_language("Tylerland")

    ### The Problem

    What if there was accidentally a duplicate country in your CSV file? Maybe the file contained an additional (and incorrect) line of `Brazil,Spanish`.
    What if there was accidentally a duplicate country in your CSV file? Maybe the file contained an additional (and incorrect) line: `Brazil,Spanish`.

    We'd see a compiler warning, which is not helpful at narrowing down which country was the duplicate:

    @@ -100,7 +104,7 @@ defmodule Country do
    end
    ```

    Now when we compile the code, we'll get a more helpful warning (along with the original warning)
    Now when we compile the code, we'll get a more helpful preceeding the warning from the Elixir compiler:

    ```bash
    $ mix compile --force
  4. TylerPachal revised this gist Sep 18, 2023. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions custom-compile-time-warnings-in-elixir.md
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,9 @@
    Custom Compile-Time warnings in Elixir
    Custom Compile-Time Warnings in Elixir Using IO.warn/1
    ---

    When you are writing macros which run at compile-time, it can be useful to emit custom compile-time warnings.

    [Skip to the answer](#the-solution)
    [Skip down to The Solution](#the-solution)

    ### Example Scenario

    @@ -70,7 +70,7 @@ warning: this clause cannot match because a previous clause at line 8 always mat

    ### The Solution

    It would be better if we emitted a more helpful error message (after idenitfying non-unique country keys). This can be done using `IO.warn/1`.
    It would be better if we emitted a more helpful error message (after idenitfying non-unique country keys). This can be done using [`IO.warn/1`](https://hexdocs.pm/elixir/1.12.3/IO.html#warn/1).

    ```elixir
    defmodule Country do
  5. TylerPachal created this gist Sep 18, 2023.
    120 changes: 120 additions & 0 deletions custom-compile-time-warnings-in-elixir.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    Custom Compile-Time warnings in Elixir
    ---

    When you are writing macros which run at compile-time, it can be useful to emit custom compile-time warnings.

    [Skip to the answer](#the-solution)

    ### Example Scenario

    Say you are using a text file to dynamically function clauses. Our function is called `official_language/1` and it takes the name of a country, and returns a list of its official languages (some countries have more than one!). Foruntaley, we have been provided with a `official_languages.csv` file that contains the mappings we need to perform this lookup.

    Implementing this with metaprogamming would look like this:

    ```elixir
    defmodule Country do
    @external_resource "priv/official_languages.csv"
    @contents File.read!("priv/official_languages.csv")

    rows = NimbleCSV.RFC4180.parse_string(@contents)

    for [country, languages] <- rows do
    languages_list = String.split(languages, ",")

    def official_language(unquote(country)) do
    {:ok, unquote(languages_list)}
    end
    end

    def official_language(_country), do: {:error, :unknown_country}
    end
    ```

    _(Read more about `@external_resource` [here](https://hexdocs.pm/elixir/1.12.3/Module.html#module-external_resource))_

    If you have the following `official_languages.csv` file in your `priv` repo, you'd be able to do the following function calls:

    ```text
    Belgium,"Dutch,French,German"
    Brazil,Portugese
    Canada,"English,French"
    Mexico,Spanish
    Italy,Italian
    ```

    ```bash
    iex(1)> Country.official_language("Canada")
    {:ok, ["English", "French"]}

    iex(2)> Country.official_language("Tylerland")
    {:error, :unknown_country}
    ```

    ---

    ### The Problem

    What if there was accidentally a duplicate country in your CSV file? Maybe the file contained an additional (and incorrect) line of `Brazil,Spanish`.

    We'd see a compiler warning, which is not helpful at narrowing down which country was the duplicate:

    ```bash
    $ mix compile --force

    Compiling 1 file (.ex)
    warning: this clause cannot match because a previous clause at line 8 always matches
    lib/country.ex:10
    ```

    ---

    ### The Solution

    It would be better if we emitted a more helpful error message (after idenitfying non-unique country keys). This can be done using `IO.warn/1`.

    ```elixir
    defmodule Country do
    @external_resource "priv/official_languages.csv"
    @contents File.read!("priv/official_languages.csv")

    rows = NimbleCSV.RFC4180.parse_string(@contents)

    # New part here!
    countries = Enum.map(rows, fn [country, _] -> country end)
    non_unique_countries = countries -- Enum.uniq(countries)
    if non_unique_countries != [] do
    IO.warn("""
    Non-unique country in #{@external_resource}: #{inspect(non_unique_countries)}
    """)
    end

    for [country, languages] <- rows do
    languages_list = String.split(languages, ",")

    def official_language(unquote(country)) do
    {:ok, unquote(languages_list)}
    end
    end

    def official_language(_country), do: {:error, :unknown_country}
    end
    ```

    Now when we compile the code, we'll get a more helpful warning (along with the original warning)

    ```bash
    $ mix compile --force

    Compiling 1 file (.ex)
    warning: Non-unique country in priv/official_languages.csv: ["Brazil"]

    lib/playground.ex:11: (module)
    (elixir 1.13.3) src/elixir_compiler.erl:73: :elixir_compiler.dispatch/4
    (elixir 1.13.3) src/elixir_compiler.erl:58: :elixir_compiler.compile/3
    (elixir 1.13.3) src/elixir_module.erl:369: :elixir_module.eval_form/6
    (elixir 1.13.3) src/elixir_module.erl:105: :elixir_module.compile/5
    (elixir 1.13.3) src/elixir_lexical.erl:15: :elixir_lexical.run/3

    warning: this clause cannot match because a previous clause at line 19 always matches
    lib/playground.ex:19
    ```