Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
ef1ce30
;pkg: set version to 1.99
simonmichael Mar 23, 2026
13de6c9
;cabal: update cabal files
simonmichael Mar 23, 2026
65257d0
;doc:README: describe the 2.x branch & plans
simonmichael Feb 6, 2026
dab1283
;doc:IMPACT: new top-level "impacts" doc
simonmichael Mar 21, 2026
aec8b1a
;doc:dev: link AI, IMPACT
simonmichael Mar 21, 2026
a547fef
;pkg:stack:9.12: cleanup, bump, make non-default
simonmichael Mar 23, 2026
c41fc6b
;pkg:stack:9.14: cleanup, bump, make default
simonmichael Mar 23, 2026
cb60c0b
pkg:ui: allow vty-crossplatform 0.5
simonmichael Mar 23, 2026
ce4d9b0
dev: MixedAmount: keep amounts with different cost basis separate
simonmichael Jan 31, 2026
eca6e7b
dev!: Hledger.Data.Types: move isAccountSubtypeOf to new module, add …
simonmichael Feb 6, 2026
536ccca
;cabal: update cabal files
simonmichael Feb 6, 2026
db7679d
feat: classify cost basis postings, adding a ptype tag
simonmichael Feb 5, 2026
c95b181
dev!: rename PostingType/RegularPosting/ptype to PostingRealness/Real…
simonmichael Feb 5, 2026
a6bd161
;doc: Cost basis / Lot syntax: update
simonmichael Feb 5, 2026
a07659c
;doc: update embedded manuals
simonmichael Feb 5, 2026
dc93e47
feat: lotful commodities; infer cost basis from transacted cost
simonmichael Feb 5, 2026
cd0e924
imp: commodities: support tag: queries
simonmichael Feb 5, 2026
06dab5c
imp: infer transacted cost from cost basis, when appropriate
simonmichael Feb 5, 2026
4a19f98
fix: print: preserve empty {} cost basis annotations
simonmichael Feb 6, 2026
23a1ebf
imp:print: show cost basis before transacted cost, like beancount
simonmichael Feb 6, 2026
5433fb0
imp: print: beancount output: handle account and commodity tags better
simonmichael Feb 6, 2026
96484c7
imp: print: beancount: provide a commented tolerance option; cleanups
simonmichael Feb 6, 2026
4fba802
imp: print: beancount output: convert market prices
simonmichael Feb 6, 2026
072ccd0
feat: journal: accounts can be declared lotful too, with a "lots" tag
simonmichael Feb 6, 2026
03cec98
imp: print: beancount: use account's lots tag value as disposal method
simonmichael Feb 6, 2026
153edb2
imp: print: beancount: balance assignments are converted to explicit …
simonmichael Feb 6, 2026
f1db35c
imp: print: beancount: convert a top-level revenue(s) account to Income
simonmichael Feb 6, 2026
194d773
imp: print: beancount: convert the no-symbol commodity to "CC"
simonmichael Feb 6, 2026
b27b2f5
imp: print: beancount: increase the example tolerance
simonmichael Feb 6, 2026
28570e0
imp: print: beancount: also convert single-letter commodity symbols
simonmichael Feb 6, 2026
4eb5f1f
;doc: SPEC-lots: draft specification for lot-related functionality
simonmichael Feb 14, 2026
1c00676
feat:lots: with the --lots flag, track and show acquired lots automat…
simonmichael Feb 14, 2026
bc59d80
imp:lots: handle disposals, in FIFO order
simonmichael Feb 14, 2026
164f020
;doc: SPEC-finalising: retroactive specification for journal finalising
simonmichael Feb 16, 2026
d04ff0c
imp:lots: handle transfers, in FIFO order (part 1)
simonmichael Feb 16, 2026
c773261
imp:lots: transfers (part 2): use a better sequence in finalise pipeline
simonmichael Feb 16, 2026
5e27f5d
;cabal: update cabal files
simonmichael Feb 16, 2026
d513216
dev:lots: reorganise current and in-progress lot tests
simonmichael Feb 17, 2026
22791cf
dev:lots: review tests, update some expected behaviours
simonmichael Feb 17, 2026
2a9f1fd
;doc: SPEC-print: document some print behaviours as a specification
simonmichael Feb 17, 2026
522d3db
;doc: SPEC-finalising: cleanup
simonmichael Feb 17, 2026
7a1bd38
dev:lots: make some tests pass
simonmichael Feb 17, 2026
d9c7ea2
dev:lots: make more tests pass
simonmichael Feb 18, 2026
c8b6bbe
;doc: SPEC-finalising: update
simonmichael Feb 18, 2026
0f42914
dev:lots: infer cost basis for bare disposals on lotful commodities
simonmichael Feb 18, 2026
5580898
dev:lots:refactor: reorganise Lots.hs by pipeline stage
simonmichael Feb 18, 2026
4579e72
dev:refactor: clean up journalFinalise
simonmichael Feb 18, 2026
3d8431d
;doc:lots: SPEC, PLAN updates
simonmichael Feb 18, 2026
594fcc7
dev:lots: consistent field order in CostBasis definition
simonmichael Feb 18, 2026
d685e1c
imp:lots: also infer cost basis from cost when {} is present
simonmichael Feb 18, 2026
76696ca
imp:lots: disposal-aware transaction balancing
simonmichael Feb 18, 2026
f9141ec
imp:journal: accept hledger consolidated lot syntax in journal input
simonmichael Feb 18, 2026
550fe49
imp:print: show hledger lot syntax by default
simonmichael Feb 18, 2026
1b5c354
imp:print: add -O ledger output format for Ledger-style lot syntax
simonmichael Feb 18, 2026
aef8f7e
imp:lots: add configurable reduction methods (FIFO, FIFO1, LIFO, LIFO1)
simonmichael Feb 19, 2026
24412e6
fix!:journal: always exclude gain postings from txn balancing
simonmichael Feb 19, 2026
79c2a68
;doc: lots: note scenarios where cost basis differs from transacted cost
simonmichael Feb 19, 2026
fb8ac90
fix:lots: show correct per-lot quantities in multi-lot disposals
simonmichael Feb 19, 2026
cc20417
imp:lots: infer amountless gain postings in disposal balancing
simonmichael Feb 19, 2026
ccc76c9
imp:lots: validate lots tag values; check SPECID method
simonmichael Feb 19, 2026
e911ca6
imp:lots: show source position in lots: tag validation errors
simonmichael Feb 19, 2026
2335525
fix:lots: fix disposal matching with explicit lot subaccounts
simonmichael Feb 19, 2026
af6f4f4
;test:lots: add tests for implicit multi-lot disposals
simonmichael Feb 19, 2026
7a7b47c
imp:lots: infer gain posting in disposal when none is present
simonmichael Feb 19, 2026
8aa948a
imp:lots: default gain account to revenue:gains when no Gain account …
simonmichael Feb 19, 2026
ff49d1a
imp:lots: add _ptype:gain tag to gain postings
simonmichael Feb 19, 2026
e958f44
imp:lots: make validateUserLabels error verbose
simonmichael Feb 19, 2026
27aaab2
imp:lots: make all Lots.hs errors verbose (position + excerpt)
simonmichael Feb 19, 2026
bc149ae
;doc: Output formats, print: ledger output format
simonmichael Feb 19, 2026
b7e8d01
;doc: update cost basis / lots docs; new Lot reporting section
simonmichael Feb 19, 2026
36eaf62
;doc: recent dev notes
simonmichael Feb 19, 2026
b2566f4
;doc:lots: SPEC/PLAN updates
simonmichael Feb 19, 2026
1a8b674
;dev: comment update
simonmichael Feb 19, 2026
7c98531
dev:lots: errors cleanup
simonmichael Feb 19, 2026
a5e3b1f
;dev: fix warning
simonmichael Feb 20, 2026
cff5071
feat:lots: add AVERAGE and HIFO reduction methods
simonmichael Feb 20, 2026
2cb8925
;test:lots: fix test expectations for cost->price rename
simonmichael Feb 20, 2026
5621adf
imp:lots: detect bare acquisitions on lotful commodities
simonmichael Feb 20, 2026
0cdd28f
fix:lots: exclude zero-amount postings from bare acquire detection
simonmichael Feb 20, 2026
3ea2c82
fix:lots: declassify bare lotful postings when cost/price can't be in…
simonmichael Feb 20, 2026
8202b05
fix:lots: detect bare lotful transfers without {} on the from posting
simonmichael Feb 20, 2026
8ad6801
fix:lots: handle bare transfers and exclude virtual postings
simonmichael Feb 20, 2026
8f1edf2
fix:lots: detect transfers more robustly
simonmichael Feb 20, 2026
e823264
fix:lots: require exact quantity matching for transfer detection
simonmichael Feb 20, 2026
f7597f6
imp:lots: move balance assertion to generated parent posting on lot s…
simonmichael Feb 26, 2026
d3fef5b
;doc:lots: document balance assertion handling with lot splitting
simonmichael Feb 26, 2026
e628094
imp:lots: add the generated-posting tag to generated gain postings
simonmichael Feb 26, 2026
4b6fe60
;doc: added Reporting concepts > Detecting special postings
simonmichael Feb 26, 2026
3d3b1fa
;dev:print: refactor
simonmichael Feb 26, 2026
923eaee
feat:lots: parse lot subaccount names into cost basis
simonmichael Feb 26, 2026
9792e6b
imp:lots, check: check accounts now always ignores lot subaccounts
simonmichael Feb 26, 2026
16b6c98
;doc: PLAN updates, add SPEC-special-postings
simonmichael Feb 26, 2026
21d99a6
feat:lots: support symmetric equity transfers in close --clopen --lots
simonmichael Feb 27, 2026
e5761b0
fix:lots: fix lot-split amounts having insufficient display precision
simonmichael Feb 28, 2026
69008fc
;doc:PLAN-lots
simonmichael Mar 1, 2026
884f644
;doc: AI.md: start a project AI policy doc
simonmichael Mar 1, 2026
919a254
;doc:AI: mention value for lot tracking
simonmichael Mar 1, 2026
eb98fe1
imp:lots: remove AVG/AVG1/ACB reduction method aliases
simonmichael Mar 1, 2026
82ce14c
;doc: Lot reporting: edits
simonmichael Mar 1, 2026
86a69b5
;tools:Shake: use newer shake to fix error with ghc 9.12.2
simonmichael Mar 1, 2026
a9ead06
;doc: update command docs
simonmichael Mar 1, 2026
ff25dff
;doc: update embedded manuals
simonmichael Mar 1, 2026
87d936f
;doc:AI
simonmichael Mar 1, 2026
408b010
;doc:AI
simonmichael Mar 1, 2026
8f3f304
imp:print:ledger output: show cost basis annotations in DLC order
simonmichael Mar 1, 2026
7376b42
dev: fix some more issues noted by copilot
simonmichael Mar 1, 2026
bfb26e6
dev:lots: update test
simonmichael Mar 2, 2026
b165ceb
dev:lots: guard against division by zero in poolWeightedAvgCost
simonmichael Mar 2, 2026
6bb2e1a
imp:print: in --lots mode, consistently show the inferred lot cost basis
simonmichael Mar 3, 2026
4c904fd
dev:lots: unify bare/non-bare paths in mkTransferPostings
simonmichael Mar 3, 2026
edd6623
imp:lots: print --lots preserves user's original amount annotations
simonmichael Mar 3, 2026
ba6c1a7
dev:lots: extract amountNormalizeCostToUnit helper
simonmichael Mar 3, 2026
320542c
dev:lots: extract amountSetQuantityOf helper
simonmichael Mar 3, 2026
430518e
dev: apply cost conversion before aggregation in valuationAfterSum path
simonmichael Mar 3, 2026
b052e32
fix:lots: simplify internals, avoid inferring bogus conversion costs
simonmichael Mar 4, 2026
0952775
imp:lots: allow bare disposals without price to get lot subaccounts
simonmichael Mar 4, 2026
44eb8bc
fix:lots: don't classify costless lotful revenues as transfer-to
simonmichael Mar 4, 2026
d4c9e89
fix:lots: preserve posting order; fix bare dispose classification
simonmichael Mar 5, 2026
848a474
fix:lots: make all reduction methods per-account; remove *1 variants
simonmichael Mar 5, 2026
ebbd2d1
imp:lots: prepend ptype tags at start of posting comments
simonmichael Mar 5, 2026
60c9cb4
;doc:PLAN-lots: some plans for recognising transfers with fees
simonmichael Mar 5, 2026
12377e4
imp:lots: detect transfer+fee patterns (inexact quantity transfers)
simonmichael Mar 5, 2026
7f50cc6
imp:lots: add dbg5 traces to lot state changes
simonmichael Mar 5, 2026
8b60f07
imp:lots: enrich error messages with lot details
simonmichael Mar 5, 2026
7963765
imp:lots: add --lots-warn flag to warn instead of error
simonmichael Mar 5, 2026
cc78e87
imp:lots: show transaction location in debug traces
simonmichael Mar 5, 2026
15c511c
imp:lots: widen --lots-warn to cover transfer pairing errors
simonmichael Mar 5, 2026
a4f0d11
imp:lots: add global reduction methods (FIFOALL, LIFOALL, HIFOALL, AV…
simonmichael Mar 5, 2026
6aca7b6
imp:lots: show method source and review hint in lot errors
simonmichael Mar 6, 2026
8405809
imp:lots: highlight specific posting in lot errors
simonmichael Mar 6, 2026
e6f0214
imp:lots: suppress mismatched transfer postings warning for now
simonmichael Mar 6, 2026
c027559
imp:lots: warn about unclassified lotful postings with --lots-warn
simonmichael Mar 6, 2026
d67fc8d
fix:lots: fix a miscounting of same-lot transfers on the same date
simonmichael Mar 6, 2026
3a74149
imp:lots: improve hints in lot error messages
simonmichael Mar 6, 2026
e1e75f4
fix:lots: avoid a case of transfer corruption caused by fee
simonmichael Mar 6, 2026
d8bb81d
fix:lots: auto-generate label on duplicate lot id collision
simonmichael Mar 6, 2026
7405cd1
;doc:PLAN-lots
simonmichael Mar 6, 2026
e0093d0
;examples: move some stuff to examples/lots/
simonmichael Mar 7, 2026
ce54e67
feat:check: add lots check
simonmichael Mar 7, 2026
27ae0fd
imp:lots: simplify unbalanced disposal error message
simonmichael Mar 7, 2026
265677b
fix:lots: don't create lot subaccounts on revenue/expense accounts
simonmichael Mar 7, 2026
7aa1bcb
;examples:lots/lot-entries.journal, sample journal entries
simonmichael Mar 7, 2026
e662aeb
feat:check: add lotswarn check with two-pass diagnostics
simonmichael Mar 7, 2026
4ff5405
imp:lots: warn when a bare acquire has no inferable cost basis
simonmichael Mar 7, 2026
e73afad
fix:lots: make bare acquire with no inferable cost a hard error
simonmichael Mar 7, 2026
c51c317
imp:lots: check lotswarn exits non-zero on warnings
simonmichael Mar 7, 2026
27b5e6e
imp:lots: make all lot warnings hard errors, drop --lots-warn/check l…
simonmichael Mar 7, 2026
3b006e0
imp:lots: exempt zero-amount lotful postings from unclassified error
simonmichael Mar 7, 2026
605d90f
imp:lots: show precise posting location in unclassified lotful error
simonmichael Mar 7, 2026
2d558b7
;doc:Reduction methods: edits
simonmichael Mar 7, 2026
4b04da5
;doc:AI: updates
simonmichael Mar 9, 2026
6f78175
;examples: hledger.conf: how to hide explicit lots
simonmichael Mar 17, 2026
1bc136b
fix:lots: more robust parsing of lot subaccount names with commas
simonmichael Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.52.99
1.99
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
# hledger

## About this branch

This is the `hledger2` branch, a development branch for the hledger 2.x releases.
This branch is mutable (it can be force-pushed).

For the hledger 1.x series, see `hledger1`.
`master` is currently the same as `hledger1`.
It will absorb `hledger2`, and possibly be renamed to `main`, when hledger 2.0 is released.

Some goals for the 2.x series:

- continue and improve 1.x's reliability
- provide excellent lot tracking and capital gains calculation
- explore ethical use of AI as a dev tool
- more cleanup and simplification of code, docs, process, finance
- more speed
- more interoperability
- more use of jj for version management
- easier contribution

and for 1.x:

- continued stability, installability
- bugfix releases to fix newly discovered regressions, if any
- preserve the non-AI-assisted codebase, and try to keep it that way

Related discussion: [Thoughts on hledger 2 #2547](https://github.com/simonmichael/hledger/issues/2547)


## Robust, intuitive plain text accounting
[![license](https://img.shields.io/badge/license-GPLv3+-brightgreen.svg)](https://www.gnu.org/licenses/gpl.html)
[![on hackage](https://img.shields.io/hackage/v/hledger.svg?label=hackage&colorB=green)](https://hackage.haskell.org/package/hledger)
Expand Down
1 change: 1 addition & 0 deletions Shake.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env stack
{- stack script --resolver nightly-2025-09-30 --compile
--extra-include-dirs /Library/Developer/CommandLineTools/SDKs/MacOSX12.1.sdk/usr/include/ffi
--extra-dep shake-0.19.9
--package base-prelude
--package directory
--package extra
Expand Down
55 changes: 55 additions & 0 deletions doc/AI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# AI policy

The productivity benefits from AI-assisted software development are becoming obvious.
The many potential costs and risks will keep becoming clearer.

Here are some current policies for AI use in the hledger project:

- hledger does not use AI at runtime.

- hledger 1.x (2007..2025) has been developed without AI assistance.

- hledger 2.x (2026..) is developed with careful AI assistance.

- This bears repeating: careful AI assistance is not "vibe coding".
We aim to increase quality and maintainability, not decrease them.

- The codebase remains human maintainable. We can always stop using AI and keep moving forward.

- We aim to use only the more principled/trustworthy/sustainable tools and providers.
For now that means we prefer Anthropic, Ecosia, local LLMs, and such.

- We monitor and try to limit and optimise our AI resource usage (represented by tokens, cost, etc.)

- We monitor the impact of AI tools on the project, ourselves, and the planet and make adjustments as needed.

Justification for AI use in this project:

- I needed it to fully design and implement robust tax lot tracking in hledger.
This is a feature that I have been wanting for years, but it was just too big/intricate to tackle.
Use of AI tools made it possible. I think it's unlikely hledger would have ever got this feature without them.

- Although similar features exist in other free software (Beancount/Ledger/rustledger/BittyTax/rotki/RP2/..),
I believe this new implementation provides flexibility currently not available elsewhere -
private, plain text, and capable of modelling real world lot operations and US pre- and post-2025 booking methods.
This (I hope) will provide value to many.

- I imagine it is overall more efficient in resources and human energy,
for developers to use AI to develop efficient deterministic software,
than to have everyone using AI individually to try and do the same tasks less efficiently and less reliably.
Ie, let's move the AI use upstream as far as possible - use it briefly at design/implementation time,
not repeatedly at usage time.

- The "bitter lesson" says that general computation always eventually wins over special-purpose systems -
suggesting that the lifetime and value of specialised tools like hledger will decrease.
However, there is at least a time lag, and for some time yet there will be a gap in efficiency, cost, reliability,
and so on, making this work worthwhile.

- We are making mindful limited use of unsustainable technologies for a short time,
in preparation for more sustainable versions (local LLMs, ASIC LLMs) coming soon.

- It is a learning experiment that can be discontinued or even rolled back at any time.

If you are a hledger user who objects to any use of AI, for one reason or another: I can understand.
AI is a tool, probably too dangerous for us, but it's here and we're going to have to try to survive it.
The AI-free hledger 1.x still exists, will continue to receive at least regression fixes, and can be revived or forked at any time if needed.
18 changes: 18 additions & 0 deletions doc/IMPACT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Impact

In the 21st century we are living in a time of collapse and disruption of many natural and human systems.

As hledger users, contributors and especially developers,
we all are responsible and accountable for this project's impact on the world.

On this page we try to understand and describe impacts
this project has, or could have, on things outside it.
Such as, eg:

- [CLIMATE](CLIMATE.md)
- [AI](AI.md) use
- people using or needing accounting software
- FOSS developers and users
- people using or interested in Haskell
- the Plain Text Accounting community
- other PTA projects such as Ledger and Beancount
62 changes: 62 additions & 0 deletions doc/NOTE-2.x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# hledger 2.x

## Strategy

Summary of discussion [Thoughts on hledger 2 #2547](https://github.com/simonmichael/hledger/issues/2547):

### Positions

**Simon's motivations**: drop costly cruft, marketing power of "2.0", lot tracking as flagship feature, AI-era demarcation, decouple from the 3-month release cycle.

**New users (rickles42, Daniii44)**: Beancount's v2->v3 transition was a cautionary tale -- intermingled docs, broken companion tools, unclear what works with which version. Don't repeat that.

**adept (collaborator)**: don't break compatibility without a clear goal that requires it. History is full of needless rewrites that killed projects.

### Recommended: "Boring 2.0"

Stay on master, release 2.0 when ready. Don't maintain parallel long-lived branches.

1. **Linear release path**: 1.52 -> 1.99.1 (preview) -> 1.99.2 -> ... -> 2.0. Avoids the cost of maintaining two diverging trunks.

2. **Minimise breaking changes; make them opt-in first**: new behaviours behind flags (like `--lots`), old behaviours deprecated with warnings for a release or two, then removed. Proven pattern (Rust editions, Python `__future__`, GHC extensions).

3. **Lot tracking alone justifies 2.0**: it's a large, data-model-impacting feature. Combined with accumulated improvements, it's enough. Bundling too many breaking cleanups risks the Beancount trap.

4. **Keep docs unified**: a single docset with "New in 2.0" / "Changed in 2.0" callouts, not two divergent doc trees.

5. **AI demarcation is worth noting but shouldn't drive versioning**: it's a process change, not user-facing. Mention in release notes, not a reason to break compatibility.

### Key Principle

Take the marketing win (call it 2.0) but keep the technical disruption minimal.

### Current most likely plan:

- Keep using one master branch.
- On next release day (march 1st), release both 1.52 (minor updates) and 1.99.1 (2.0 preview 1, with lot tracking).
- Don't intentionally break anything, except in the usual way (as rarely as possible, with easy workarounds and deprecation periods).

## Goals

SM 2026-02:\
Some goals for 2.x:

- continue and improve 1.x's reliability
- excellent lots/capital gains tracking
- more interoperability/convertibility
- more speed
- more customisation paths

and:

- more use of AI as a dev tool; clarify policies
- more use of jj to simplify version management
- more aggressive cleanup and simplification of code/doc/process/finance..
- easier contribution

and for 1.x:

- continued installability/usability
- preserve the stable/known hledger 1.x feature set
- preserve the non-AI-assisted codebase; draw a line between pre and post-AI eras

138 changes: 138 additions & 0 deletions doc/NOTE-amount-classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Typeclass Semantics for Amounts

**Date:** 2026-02-01
**Context:** Design question on whether to define amount semantics via Monoid first, then Num, or vice versa.

## Question

Is it better to define amounts' semantics in terms of standard typeclasses like Monoid first, and then define Num operations in terms of those?

## Current State

### MixedAmount (hledger-lib/Hledger/Data/Amount.hs:848-863)

```haskell
instance Semigroup MixedAmount where
(<>) = maPlus

instance Monoid MixedAmount where
mempty = nullmixedamt

instance Num MixedAmount where
(+) = maPlus -- delegates to Monoid operation
(*) = error "..." -- intentionally partial
signum = error "..." -- intentionally partial
```

### Amount (hledger-lib/Hledger/Data/Amount.hs:300-307)

```haskell
instance Num Amount where
(+) = similarAmountsOp (+) -- errors if commodities differ
(*) = similarAmountsOp (*) -- questionable semantics
-- No Semigroup/Monoid instance at all
```

## Analysis

### MixedAmount Follows Monoid-First Design

**Strengths:**
- ✅ Core semantics defined via `maPlus :: MixedAmount -> MixedAmount -> MixedAmount` (line 905-906)
- ✅ Num instance delegates to Monoid (`(+) = maPlus`)
- ✅ Explicitly rejects nonsensical operations (multiplication, signum)
- ✅ Matches DESIGN-amounts-keys.md observation: "MixedAmount forms a commutative monoid" (line 114-121)

### Amount Does NOT

**Problems:**
- ❌ Only has Num, no Monoid/Semigroup
- ❌ Can't have a proper Monoid (no identity: `nullamt + $1` ≠ `$1` due to commodity difference)
- ❌ Num instance is **partial** (errors on mismatched commodities)
- ❌ Implements `(*)` with unclear semantics

## Recommendation: Monoid-First is Better

### Reasons

1. **Semantic Clarity**
- The key operation is aggregation (combining amounts)
- Monoid captures exactly this - no more, no less
- When deciding "what goes in the key?", you're really asking "when does `<>` combine vs keep separate?"

2. **Partial Num is a Code Smell**
- If you need `error` in Num methods, the type doesn't truly satisfy the Num contract
- Better to not have the instance than to have a partial one

3. **Aligns with Design Documents**
- DESIGN-amounts-keys.md explicitly identifies the commutative monoid structure as fundamental (line 114-121)
- The core operation `add1` (line 123-129) is monoid addition

4. **Better Composition**
- Monoid instances compose beautifully (e.g., `Map k v` is a Monoid if `v` is)
- This helps with the `MixedAmount = Map Key Amount` design

5. **Forces Clear Thinking**
- Monoid has simpler laws (associativity + identity)
- Easier to verify and test
- Num brings unnecessary baggage (multiplication, division, etc.)

### Theoretical Foundation

From abstract algebra perspective:

- **Monoid**: Just needs associativity, identity, and closure - minimal structure
- **Num**: Implies ring-like structure with multiplication - too much structure for amounts
- **For amounts**: Addition forms a commutative monoid, but multiplication is not well-defined
- What is `$1 * $1`? `$1`? Type error? `$²1`?
- What is `$1 * €1`? Nonsensical.

## Concrete Suggestions

### Short Term (Low Risk)
- Keep current MixedAmount design (already follows Monoid-first)
- Document that Num instance is a convenience wrapper around Monoid
- Consider deprecation warnings on partial operations

### Medium Term (Moderate Risk)
- Drop Amount's Num instance entirely
- Add clear utility functions for amount arithmetic:
```haskell
amountPlus :: Amount -> Amount -> Either String Amount
-- Returns Left if commodities don't match
```

### Long Term (Requires Migration)
- Consider Amount Semigroup instance IF there's a sensible (<>) that doesn't require same commodity
- Or: Keep Amount without Monoid (since no proper identity) but use Semigroup for combining
- Make MixedAmount the primary abstraction for all arithmetic

## Tradeoffs

**Lost Convenience:**
- Can't write `amount1 + amount2`
- Must write `amount1 <> amount2` or explicit function calls

**But:**
- This is already partial anyway (errors on commodity mismatch)
- Making it explicit forces handling the error case
- More honest API

## Related

- **DESIGN-amounts-keys.md** - Discusses MixedAmount monoid structure
- **hledger-lib/Hledger/Data/Amount.hs:905** - `maPlus` implementation
- **hledger-lib/Hledger/Data/Amount.hs:848** - Current Semigroup/Monoid instances

## Open Questions

1. Should Amount have a Semigroup instance? What would `<>` mean for amounts with different commodities?
2. If we remove Num from Amount, what impact on existing code?
3. Should we use a different type for "amounts that can be added" vs "amounts that must be kept separate"?
4. Could we use a phantom type to track whether an Amount is "simple" (can add) vs "complex" (need MixedAmount)?

## Conclusion

**Monoid-first design is theoretically cleaner and aligns better with amount semantics.** The current MixedAmount implementation already follows this pattern. Amount's Num instance is a historical convenience that causes more confusion than it solves.

Recommendation: Embrace Monoid as the primary abstraction for amount aggregation, with Num as a thin, well-documented convenience layer that explicitly delegates to Monoid operations.
Loading
Loading