Trouble installing combinator and dictionary modules

Hello!
I am trying to run a code example from the book “The pragmatic programmer” but it’s not working. I have never used Elixir (or any other functional programming language) before, so the solution to my problem might be simple.

Versions:

Erlang/OTP 27 [erts-15.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]

Elixir 1.18.1 (compiled with Erlang/OTP 27)

The script I am trying to run is an example from the “The pragmatic programmer” book, aiming to display the advantages of pipeline operators. This code takes a word as input and outputs all possible word combinations that match an English dictionary.

anagrams/lib/anagrams.exs

defmodule Anagrams do

  def anagrams_in(word) do
    word
    |> all_subsets_longer_than_three_characters()
    |> as_unique_signatures()
    |> find_in_dictionary()
    |> group_by_length()
  end

  defp all_subsets_longer_than_three_characters(word) do
    word
    |> String.codepoints()
    |> Comb.subsets()
    |> Stream.filter(fn subset -> length(subset) >= 3 end)
    |> Stream.map(&List.to_string(&1))
  end

  defp as_unique_signatures(subsets) do
    subsets
    |> Stream.map(&Dictionary.signature_of/1)
  end

  defp find_in_dictionary(signatures) do
    signatures
    |> Stream.map(&Dictionary.lookup_by_signature/1)
    |> Stream.reject(&is_nil/1)
    |> Stream.concat(&(&1))
  end

  defp group_by_length(words) do
    words
    |> Enum.sort()
    |> Enum.group_by(&String.length/1)
  end

end

When running this code in iex with iex -S mix and then c("lib/anagrams.exs") I receive the following warnings:

 warning: Comb.subsets/1 is undefined (module Comb is not available or is yet to be defined). Make sure the module name is correct and has been specified in full (or that an alias has been defined)
    │
 14 │     |> Comb.subsets()
    │             ~
    │
    └─ (anagrams 0.1.0) lib/anagrams.exs:14:13: Anagrams.all_subsets_longer_than_three_characters/1

    warning: Dictionary.signature_of/1 is undefined (module Dictionary is not available or is yet to be defined). Make sure the module name is correct and has been specified in full (or that an alias has been defined)
    │
 21 │     |> Stream.map(&Dictionary.signature_of/1)
    │                               ~
    │
    └─ (anagrams 0.1.0) lib/anagrams.exs:21:31: Anagrams.as_unique_signatures/1

To resolve these problems, I tried to find adequate modules (comb and dictionary) and to add them to the dependencies in mix.exs:

  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
      {:comb, git: "https://github.com/tallakt/comb.git"},
      {:dictionary, "~> 0.1.1"}
    ]
  end

Fetching them (mix deps.get) seems to work:

➜  anagrams mix deps.get
* Getting comb (https://github.com/tallakt/comb.git)
remote: Enumerating objects: 232, done.        
remote: Total 232 (delta 0), reused 0 (delta 0), pack-reused 232 (from 1)        
origin/HEAD set to master
Resolving Hex dependencies...
Resolution completed in 0.018s
New:
  dictionary 0.1.1
* Getting dictionary (Hex package)

But rerunning the script afterward results in the same warnings.

I would be thankful if anyone could tell me what I am missing or what additional information I should provide to resolve this.
Kind regards!

When you run iex -S mix in the project folder, it should automatically compile your module and all the dependencies. You don’t need to manually compile it. What happens when you simply do iex -S mix from within the anagrams directory and once it loads, Anagrams.anagrams_in("abc")?

Hi vkryukov,
thanks for your suggestion and the quick reply.

Here is the output:

anagrams iex -S mix
Erlang/OTP 27 [erts-15.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]

Interactive Elixir (1.18.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Anagrams.anagrams_in("abc")
** (UndefinedFunctionError) function Anagrams.anagrams_in/1 is undefined or private
    (anagrams 0.1.0) Anagrams.anagrams_in("abc")
    iex:1: (file)

Kind regards!

This is certainly not normal. Somehow even your function is not found, that has nothing to do with the dependencies.

If you can post your project to a public GitHub, people here can help you better.

I found a couple of issues.

  1. lib/anagrams.exs should be lib/anagrams.ex. That should fix the Anagrams.anagrams_in/1 is undefined or private error.
    .exs is the elixir script extension and isn’t used for modules in your lib folder, you will see it in the tests folder created by mix new as the test scripts are not precompiled.

  2. Dictionary is a module provided in the Pragmatic Programmer source zip file here (/code/function-pipelines/anagrams/lib/dictionary.ex), not the module published on hex.pm, the only external dependency is Comb which you are correctly using :slight_smile:. You can copy it into your anagrams/lib folder.

  3. There are 2 bugs in the code provided by the publisher!
    a. in the function Anagrams.find_in_dictionary/1 the stream does not flatten the lists of results found

    |> Stream.concat(&(&1))
    

    should be

    |> Stream.concat()
    

    and
    b. The dictionary module has a blank map in the @dict attribute with the correct precompiled map commented out.

    defmodule Dictionary do
      @dict %{} #DictReader.read_dict
      ...
    

    should be

    defmodule Dictionary do
      @dict DictReader.read_dict
      ...
    
2 Likes