You could avoid subtraction entirely and rely on pattern-matching instead:
defmodule RomanParser do
def parse(input, total \\ 0)
def parse("M" <> rest, total), do: parse(rest, total + 1000)
def parse("CM" <> rest, total), do: parse(rest, total + 900)
def parse("D" <> rest, total), do: parse(rest, total + 500)
def parse("CD" <> rest, total), do: parse(rest, total + 400)
def parse("C" <> rest, total), do: parse(rest, total + 100)
def parse("XC" <> rest, total), do: parse(rest, total + 90)
def parse("L" <> rest, total), do: parse(rest, total + 50)
def parse("XL" <> rest, total), do: parse(rest, total + 40)
def parse("X" <> rest, total), do: parse(rest, total + 10)
def parse("IX" <> rest, total), do: parse(rest, total + 9)
def parse("V" <> rest, total), do: parse(rest, total + 5)
def parse("IV" <> rest, total), do: parse(rest, total + 4)
def parse("I" <> rest, total), do: parse(rest, total + 1)
def parse("", total), do: total
end
RomanParser.parse("MMMDXVI")
(nimble_parsec can produce similar code with less typing, fwiw)
Both this solution and the other one up-thread have a similar bug/feature: they accept incorrect numbers and do their best to render them. For instance, "IIIIVM" is parsed to 1006 by both and "IM" is parsed to 1001 by this solution and 999 by the subtraction method.






















