{"slug": "does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem", "title": "Does anyone want to take over maintenance of the Kowainik ecosystem?", "summary": "A contributor using Anthropic's Claude has submitted a pull request to the Kowainik tomland Haskell library adding full TOML 1.1.0 support, with the parser now accepting all 218 valid documents and rejecting all 494 invalid ones in the official toml-test suite's files-toml-1.1.0 list. The change, which closes issues #373, #248 and #264, is API-breaking and bumps the version to 1.4.0.0, replacing the old TOML AST with a map from a single Piece to an Entry and adding a sequential-pass validator with a new ExtendClosedTable error. The pull request also asks whether anyone wants to take over maintenance of the Kowainik ecosystem, noting tomland had fallen behind the spec.", "body_md": "Hi! I really like tomland, especially for its bidirectional codecs, and it made […](https://github.com/kowainik/tomland/pull/470)me a bit sad to see it fall behind the spec.\nNow that I have Claude at my side, I thought I'd put in the effort to push through some changes that were planned years ago (#248, #264, #373). There are a few of them and they are necessarily API-breaking, so I made sure to split them into individual commits that add up to full TOML 1.1.0 support.\nThe short version: against the official [toml-test](https://github.com/toml-lang/toml-test) suite (the `files-toml-1.1.0` list), the parser now accepts all 218 valid documents and rejects all 494 invalid ones.\nCloses #373, closes #248, closes #264.\n### What's in here\nThe commits fall roughly into three groups.\n**Parser** (`dc09f7d`..` dac66c7`). Mostly about the TOML 1.0 rules that weren't enforced, plus the 1.1 additions:\n- whitespace, newlines and comments are only consumed where the spec allows them. `sc` now consumes only spaces and tabs and a new `scn` handles newlines/comments. A key/value pair or header has to be followed by a newline;\n- whitespace around dots in keys, ASCII-only bare keys;\n- string rules: `\\e` and `\\xHH` (1.1), tabs and non-ASCII in basic strings, control characters rejected in literal strings, `\"\"` before the closing `\"\"\"`, etc.;\n- times without seconds (1.1), lowercase `t`/` z`, range-checked offsets, no leading zeros in floats or `0_1`;\n- inline tables can span lines and have a trailing comma (1.1), and can be nested in inline tables and arrays.\nThere are two printer fixes in this group too, because the round-trip tests started failing on them. `MultiLine` mode printed `key =` followed by a newline, which isn't valid TOML. Control characters were printed with `show`, so you'd get `\\ESC`, which no TOML parser accepts.\n**Mixed arrays and unquoted keys** (`c4d29d5`, `20fd317`).\n- `Array` holds `[AnyValue]` instead of `[Value t]`. There's a new `array` helper for the homogeneous case and an `_Either` BiMap to decode into a sum type.\n- `Piece` used to keep the quotes, so `key`, `\"key\"` and `'key'` were three different keys. The spec says they're the same key, and a codec had to know how the file spelled it. Now the parser strips the quotes and the printer adds them back when a piece isn't a bare key.\n**New AST and validator** (`a9b79c2`). This one is the big one and it's basically #264. The old `TOML` had two ways to encode a nested table, either as a prefix-tree sibling or as a nested sub-document, and which one you got depended on the order of headers in the file. As a result:\n- lookups and `Eq` depended on header order;\n- reopening a parent after a subtable duplicated the subtree;\n- `int \"a.b\"` and `table (int \"b\") \"a\"` read different places;\n- the validator had no idea which tables could still be extended, so it accepted 43 invalid documents from toml-test.\nNow `TOML` is a map from a single `Piece` to an `Entry`:\n``` haskell\ndata Entry\n    = EValue AnyValue\n    | ETable TableKind TOML\n    | ETableArray (NonEmpty TOML)\ndata TableKind = HeaderTable | ImplicitTable | DottedTable | InlineTable\n```\nEvery level of nesting is exactly one piece, so `a.b.c = 1`, `[a.b]` + `c = 1` and `a = { b = { c = 1 } }` all produce the same document. `TableKind` exists only so the validator can apply the 1.1 rules (inline tables are closed, dotted tables can't be reopened with a header, and so on). `Eq` ignores it. `Value` and `TOML` are now mutually recursive (`Table :: TOML -> Value 'TTable` for inline tables inside arrays), so they both live in `Toml.Type.Value`.\nThe validator is now a single sequential pass that tracks scopes, with a new `ExtendClosedTable` error.\n### Compatibility\nThis is a breaking change, so I've bumped the version to 1.4.0.0. I tried to keep the damage limited to people who work with the AST directly:\n- the codec API is unchanged, and existing codecs should keep working. `list`/` nonEmpty` read both `[[key]]` and `key = [ {..}, {..} ]`;\n- `tomlPairs`, `tomlTables` and `tomlTableArrays` are still there as deprecated read-only functions with their old types, and `Toml.Type.PrefixTree` is kept as a deprecated module. My suggestion would be to drop them in 1.5;\n- two things can't be shimmed: `Array` holding `AnyValue`, and `Piece` being unquoted. The second one is the nasty one, because `Piece \"\\\"a.b\\\"\"` still compiles but now means something different. I wrote a \"Migrating from 1.3\" section in the changelog that covers both.\n### Testing\n- `cabal test`: 328 examples, 0 failures (GHC 9.12.4 locally; I haven't tried the rest of the CI matrix yet, so let's see what CI says).\n- toml-test, 1.1.0 list: 218/218 valid accepted, 494/494 invalid rejected. tomland doesn't ship a toml-test decoder, so this is an accept/reject sweep with a small `runghc` script that calls `Toml.Parser.parse`. It doesn't check the decoded values against the expected JSON. Happy to add the script (or a proper decoder executable) to the repo if you think it's worth it.\n- For what it's worth, the 1.0.0 list gives 208/208 and 492/501. The 9 misses are exactly the things 1.1 relaxed: times without seconds, multi-line inline tables and trailing commas in them, and `\\x` escapes. There's no \"1.0 mode\", and I don't think one is needed.\n### Open questions\n- Even split into commits, it's still a lot to review. The parser commits come before the AST change and stand on their own (the test suite passes at `dac66c7`), so I can split them into a separate PR if that's easier for you.\n- I haven't measured performance. The new representation should be in the same ballpark (`HashMap` per level instead of a prefix tree), but I haven't checked.", "url": "https://wpnews.pro/news/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem", "canonical_source": "https://discourse.haskell.org/t/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem/15010#post_9", "published_at": "2026-11-09 22:54:07+00:00", "updated_at": "2026-11-09 23:20:18.456238+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Kowainik", "tomland", "Claude", "Anthropic", "TOML 1.1.0", "toml-test"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem", "markdown": "https://wpnews.pro/news/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem.md", "text": "https://wpnews.pro/news/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem.txt", "jsonld": "https://wpnews.pro/news/does-anyone-want-to-take-over-maintenance-of-the-kowainik-ecosystem.jsonld"}}