The Elixir Money library keeps the currency attached to the amount
Elixir library for working with Money safer, easier, and fun... Is an interpretation of the Fowler's Money pattern in fun.prog.
At a glance
- What is it?
- Money is an Elixir library that represents a monetary value as a struct with an integer amount and a currency atom, so arithmetic cannot silently produce a float, and it ships three Ecto types for storing that struct in an integer column, a PostgreSQL composite type or a JSON map. The last published release is 1.13.1 from July 2024, while the README asks for a 1.14 dependency.
- Who is it for?
- Adopt Money when your Elixir application stores or computes monetary amounts and you want the currency to travel with the value through arithmetic, formatting and the database layer. Do not adopt it expecting currency conversion, since the documentation here covers arithmetic, formatting and serialisation rather than exchange rates.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 57 days ago.
- What is it written in?
- Mainly Elixir, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A struct where a float would otherwise go
Money is an Elixir library for working with money, and it describes itself as an interpretation of Martin Fowler's Money pattern in functional programming. The argument in the README is a single line: you should not represent monetary values by a float, and wherever you need to represent money, use Money. The Bill Karwin quote at the top of the README makes the same point more colourfully.
The library itself is small. Money comes with no required dependencies, which matters in a language where the dependency tree of a web application is often the first thing that hurts, and the hex.pm package page is the distribution point.
The representation is the design. A value is a struct with an amount and a currency:
five_eur = Money.new(500, :EUR) # %Money{amount: 500, currency: :EUR}
ten_eur = Money.add(five_eur, five_eur) # %Money{amount: 10_00, currency: :EUR}The amount is an integer in the smallest unit of the currency, 500 for five euro, and the currency is an atom. That combination is what makes the rest of the library work: the type of the value carries its unit, so a function that takes a Money cannot be handed a bare number by accident, and arithmetic that returns a Money keeps the currency attached.
The audience is Elixir developers who have a money problem and want it solved in a way that composes with the rest of a Phoenix or Ecto application, rather than a general purpose financial library.
Arithmetic that keeps the currency in the result
The API is a set of plain functions on the struct, and the README's usage block reads like a session in the console.
hundred_eur = Money.multiply(ten_eur, 10) # %Money{amount: 100_00, currency: :EUR}
ninety_nine_eur = Money.subtract(hundred_eur, 100) # %Money{amount: 99_00, currency: :EUR}
shares = Money.divide(ninety_nine_eur, 2) # [%Money{amount: 4950, currency: :EUR}, %Money{amount: 4950, currency: :EUR}]The divide result is the detail worth pausing on. Ninety nine euro split in two does not have an exact cent representation in general, so the library returns a list of two Money values, 49.50 each, and leaves the caller to decide what to do with a remainder when it happens. The signature is a list because the result is not guaranteed to be a single value, and a caller who ignores that detail will drop money on the floor.
There are also predicates and comparisons:
Money.equals?(five_eur, Money.new(500, :EUR)) # true
Money.zero?(five_eur); # false
Money.positive?(five_eur); # trueNote the currency module as well, which answers the questions a UI usually asks:
Money.Currency.symbol(:USD) # $
Money.Currency.symbol(Money.new(500, :AFN)) # ؋
Money.Currency.name(Money.new(500, :AFN)) # AfghaniThe currency is an atom drawn from a table of ISO codes, so the currency list, its symbols and its display names are data the library carries rather than something each application invents.
Formatting options, including the ones that trip people up
Turning a Money into a string is where a money library earns its keep, because the format depends on the currency and on the locale of the reader.
The default is the currency's own convention:
Money.to_string(Money.new(500, :CNY)) # ¥ 5.00The rest are options on the same function. The separator and delimiter can be swapped for European formats, and the symbol can be suppressed:
Money.to_string(Money.new(1_234_56, :EUR), separator: ".", delimiter: ",", symbol: false)
"1.234,56"Two more options control the fractional part. fractional_unit: false drops the decimals entirely, which is what you want for a price column that stores whole units:
Money.to_string(Money.new(1_234_56, :USD), fractional_unit: false) # "$1,234"And strip_insignificant_zeros removes trailing zeros while keeping the value:
Money.to_string(Money.new(1_234_50, :USD), strip_insignificant_zeros: true) # "$1,234.5"These two pull in opposite directions, and it is worth deciding per surface rather than per library. A receipt wants the significant zeros removed so a price of 10.50 does not read as 10.5, while an accounting export may want the full fixed precision. The library gives you both and leaves the choice with the caller, which is the right place for it.
The currency symbol comes from the currency table, so a Japanese yen amount formats as ¥ and an Afghani amount as ؋ without the application holding a symbol dictionary.
Three Ecto types for three database shapes
The part of the library that saves the most work is its Ecto integration, and it comes in three shapes because the sensible storage model depends on whether a table holds one currency or many.
The first is single currency, backed by an integer column, and it is the cheapest. You set a default currency in the config file:
config :money,
default_currency: :USDThe migration uses an integer column, and the schema uses the Amount type:
create table(:jobs) do
add :amount, :integer
endschema "jobs" do
field :amount, Money.Ecto.Amount.Type
endThe trade-off is visible in the design. An integer column cannot hold the currency, so the value in the database is only meaningful together with the configured default. If a second currency ever enters that table, the data becomes ambiguous.
The second shape keeps the currency, and it is specific to PostgreSQL. Money.Ecto.Composite.Type maps the struct onto a PostgreSQL composite type, which you create yourself in a migration:
def up do
execute """
CREATE TYPE public.money_with_currency AS (amount integer, currency varchar(3))
"""
endThe column then uses that type, and the schema uses the composite Ecto type:
def change do
alter table(:jobs) do
add :price, :money_with_currency
end
endThe third shape is the database-agnostic one. Money.Ecto.Map.Type serialises the struct to a map, for JSON storage:
def change do
alter table(:jobs) do
add :price, :map
end
endschema "jobs" do
field :price, Money.Ecto.Map.Type
endSo the choice is between an integer column that assumes a currency, a PostgreSQL composite column that stores both, and a JSON map that stores both and works anywhere.
What the library does not do
The limits are worth stating because a money library invites assumptions it may not satisfy.
The documentation in this repository covers arithmetic, comparison, formatting, currency metadata and database serialisation. It does not describe currency conversion, exchange rates, or any notion of a value in one currency being worth a value in another at a point in time. If your application needs to convert between currencies, the conversion is yours to write, and the struct's design means you would write it on top of a library that deliberately keeps one currency per value.
The single currency database type is the other sharp edge. Because the column is an integer, the currency lives only in the application configuration, and the type will read any integer back as the default currency. That is fine for a single-currency table and wrong for anything else, and the README's own framing, Serialization to database with single currency, makes the scope explicit.
There is also a rounding question the library leaves visible rather than hides. Dividing money returns a list, so a caller that pattern matches on a single element has to decide what the remainder is, and the library does not pick a rounding mode for you.
None of these are defects so much as boundaries. The library is small because it does one thing, and the things it does not do are the things most teams will need to supply.
The repository layout and the Decimal question
The top level of the repository explains more about the project than the README does.
There is lib for the library, test for the test suite, mix.exs and mix.lock, and a CHANGELOG.md, CONTRIBUTING.md and LICENSE.md. The linting and formatting setup is visible too, with a .credo.exs for Credo and a .formatter.exs.
Then there are three directories that look unusual, and they explain the shape of the project. blend.exs and a blend directory are a mix of libraries built and released together, which is how a library that supports more than one major version of a dependency can publish coherent releases. The decimal_1_2 and decimal_2_0 directories are the branches of that support, one for the 1.2 series of the Decimal library and one for the 2.0 series.
That is the answer to a question the README never raises. Money does not depend on Decimal, since the amount is an integer, but applications that use Money usually do depend on Decimal for the rates and quantities around it. By carrying a 1.2 and a 2.0 line in one repository, the project can serve applications on either side of the Decimal 2.0 change, which is a compatibility problem most libraries ignore until it becomes a support burden.
The consequence for an adopter is that the version you pick on Hex may not be the version you want, depending on which Decimal your application already has, and the CHANGELOG in the repository is where that is recorded.
Versions: a 1.14 requirement and a 1.13.1 release
There is a mismatch between the installation instructions and the release history, and you should resolve it before you pin a version.
The README asks for a 1.14 dependency:
def deps do
[{:money, "~> 1.14"}]
endand then tells you to run mix deps.get. The published release list, however, ends at version 1.13.1, published on 2024-07-10, with 1.13.0 a day earlier on 2024-07-09 and 1.12.3 back on 2023-08-23.
The last push to the default branch was on 2026-08-27, so work on the repository continued well after that release, and the master branch is the likely home of the 1.14 line the README describes.
That is the state of a mature, low-churn library. The type of project where nothing breaks, the API stays put, and releases come when a dependency moves. It is also a project where you should read the CHANGELOG before upgrading rather than assume a patch release is inert, because a minor bump in a library that also supports two major versions of a dependency can carry behaviour changes for one of them.
The hex.pm page for the money package is the distribution point, with the API documentation on hexdocs.pm, and both are linked from the README badges.
Editorial conclusion
Adopt Money when your Elixir application stores or computes monetary amounts and you want the currency to travel with the value through arithmetic, formatting and the database layer. Do not adopt it expecting currency conversion, since the documentation here covers arithmetic, formatting and serialisation rather than exchange rates. Before you pin a version, reconcile the README, which asks for a ~> 1.14 dependency, with the releases, where 1.13.1 published on 2024-07-10 is the most recent, and decide early whether a single-currency integer column is enough, because that type silently assumes the configured default currency rather than storing it.
Frequently asked questions
How do I add the Money library to an Elixir project?
Money comes with no required dependencies, so you add {:money, "~> 1.14"} to the deps in mix.exs and then run mix deps.get. The package is published on hex.pm and its API documentation is on hexdocs.pm.
What does Money.divide return when the amount does not divide evenly?
It returns a list of Money values rather than a single one, so a caller has to decide what to do with any remainder. The README example divides ninety nine euro by two and gets two values of 49.50.
How do I store a Money value in an Ecto schema with a single currency?
Use the Money.Ecto.Amount.Type with an integer column, and set a default currency in the config with config :money, default_currency: :USD. Because the column is an integer, the currency is not stored with the value.
How do I keep the currency when storing Money in PostgreSQL?
Create a composite type money_with_currency as (amount integer, currency varchar(3)), add a column of that type, and use the Money.Ecto.Composite.Type in the schema. For a database-agnostic option, Money.Ecto.Map.Type serialises the value into a map column.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elixirmoney-money)Was this useful?