GreenTriangle
GreenTriangle

Reputation: 2460

No function clause, though there definitely seems to be?

I'd appreciate some help with this, it's the first thing I'm trying to do with Elixir and it's throwing me off.

So my intent is to capture from STDIN over and over, parsing the user input as numbers. When the user finally hits enter without entering a number, they get a sum of all numbers entered so far. Simple.

So here's how I plan it:

defmodule Solution do
  def main, do: scan(IO.read :line)
  def scan(line), do: scan(IO.read :line, Integer.parse line)
  def scan("\n", total), do: IO.puts(total)
  def scan(line, {total, _}) do
    final_total = Integer.parse(line) + Integer.parse(total)
    next_line = IO.read :line
    scan(next_line, final_total)
  end
end

Solution.main

Going line by line:

def main, do: scan(IO.read :line)

To start with, call scan passing in one line from stdin.

def scan(line), do: scan(IO.read :line, Integer.parse line)

If we get a scan call with a single argument, parse that argument as an integer, and pass it plus the next stdin line to scan/2.

def scan("\n", total), do: IO.puts(total)

If we get a scan/2 call where the stdin line was blank, just output the second argument, the integer total.

and then finally

  def scan(line, {total, _}) do
    final_total = Integer.parse(line) + Integer.parse(total)
    next_line = IO.read :line
    scan(next_line, final_total)
  end

We get a line from stdin, and a tuple of an integer total and some garbage. The current total is that line (parsed as int) plus the previous total. We call scan/2 again with a new line from stdin and our latest total.

All the logic seems to hold to me. But I get (FunctionClauseError) no function clause matching in IO.read/2. Elixir's error messages aren't super descriptive, so I'm having trouble working this out. What exactly am I doing wrong?

Upvotes: 2

Views: 85

Answers (1)

Dogbert
Dogbert

Reputation: 222398

The biggest hint here is the 2 in (FunctionClauseError) no function clause matching in IO.read/2. You're calling the 2 arity version of IO.read instead of 1 (with the arguments :line and Integer.parse line), which you most likely intended. This is because you're missing a pair of parenthesis around the arguments for IO.read in the 3rd line:

def scan(line), do: scan(IO.read :line, Integer.parse line)

That should be:

def scan(line), do: scan(IO.read(:line), Integer.parse line)

It's also considered more idiomatic to have parenthesis around all function calls (at least those with arguments) when you're nesting them:

def scan(line), do: scan(IO.read(:line), Integer.parse(line))

The error here is a bit less obvious than it could have been (like failing to compile) since both Solution.scan and IO.read have 1 and 2 arity versions.

Upvotes: 3

Related Questions