# Like "deriving Read" but faster?

**URL:** <https://discourse.haskell.org/t/like-deriving-read-but-faster/14585>\
**Category:** Learn\
**Created:** [August 19, 2026, 2:59pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585 "2026-08-19T14:59:21Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jwaldmann](https://avatars.discourse-cdn.com/v4/letter/j/977dab/32.png) [@jwaldmann](https://discourse.haskell.org/u/jwaldmann)\
**Post date:** [August 19, 2026, 2:59pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/1 "2026-08-19T14:59:22Z")

</div>

I could use a parser that is equivalent to the one produced by “deriving Read” but …

- A) faster, and using less space (Data.Text(.Lazy) instead of String)
- B) streaming (parse a long list literal “[0,1,2, …]” in constant space, produce the first cons of the result right after reading “[0”)
- C) with some minimal error reporting (e.g., some prefix of not-consumed input)

Perhaps attoparsec with some form of early-commited choice?

I do have something (only for A) at [https://codeberg.org/jwaldmann/text-read](https://codeberg.org/jwaldmann/text-read) (attoparsec on lazy text) and it seems twice as fast as “deriving Read”, and uses less memory, in my tests.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [August 19, 2026, 3:17pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/2 "2026-08-19T15:17:12Z")

</div>

Constant space parsing is hard with any Haskell library. See previous discussion: [Memory usage for Backtracking in Infinite Stream Parsing](https://discourse.haskell.org/t/memory-usage-for-backtracking-in-infinite-stream-parsing/1384)

(That’s not to say that I’m very happy with the solution I posted there. uu-parsinglib is a quite exotic library and also not very performant.)

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [August 19, 2026, 8:00pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/3 "2026-08-19T20:00:51Z")

</div>

> [@jwaldmann](#):
>
> - produce the first cons of the result right after reading “[0”

Not that I’m an expert in all kind of parsing libraries, but isn’t requirement B hard to get right, when you must hope for something like a closing parenthesis? Should you have passed down the 0,1,2 to the stream consumer when `blargh` is what ends the text?  
That said, especially with the monad transformer parsers you can add a `Writer` component into the stack and achieve partial output. megaparsec can lift `MonadWriter` since [9.5.0](https://hackage-content.haskell.org/package/megaparsec-9.8.1/docs/Text-Megaparsec.html#t:ParsecT). Searching for generic+parser on hackage yields [incremental-parser](https://hackage.haskell.org/package/incremental-parser) as a candidate for B.

Are you also after the convenience of `deriving Read` or is it okay if all the parsers are hand-rolled?

---

<div class="post-metadata">

**Author:** ![jwaldmann](https://avatars.discourse-cdn.com/v4/letter/j/977dab/32.png) [@jwaldmann](https://discourse.haskell.org/u/jwaldmann)\
**Post date:** [August 20, 2026, 11:11am UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/4 "2026-08-20T11:11:30Z")

</div>

Thanks for the comments and pointers.

> B hard to get right

yes. when the parser sees open-bracket, it should commit to the following parser being successful, in the sense that if the closing-bracket is later found missing, the parser uses `error` to throw an exception.

I made this proof-of-concept

```haskell
import Control.Monad.State.Lazy
import qualified Data.Text.Lazy as L
type Parser = StateT L.Text (Either String) 

```

and I think I get the streaming behaviour (see test case at top of linked file) from sprinkling the code with

```haskell
p *>! q = p *> commit q

commit :: Parser a -> Parser a
commit p = 
  StateT $ \ t -> Right $ case runStateT p t of
    Left msg -> error msg
    Right (a, s) -> (a, s)

```

[https://codeberg.org/jwaldmann/text-read/src/commit/f201c6deffb52887ae43d89e5dc546d59ca66db7/src/CMS.hs](https://codeberg.org/jwaldmann/text-read/src/commit/f201c6deffb52887ae43d89e5dc546d59ca66db7/src/CMS.hs)

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [August 20, 2026, 9:36pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/5 "2026-08-20T21:36:32Z")

</div>

`commit` is an interesting combinator. Is there prior art?  
I hope this does not derail the thread too much, but I wonder whether one can use a pure and total data structure instead of `error` for this.  
Consider: The `Generic` representation of lists is essentially:

```haskell
type Rep [a] ~ (U1 :+: (Rec0 a :*: Rec0 [a]))
type Rep1 [] ~ U1 :+: (Par1 :*: Rec1 []) 

```

In order to obtain a type of lists that can end in a parse error, all we have to do is to replace `Rec1 []` with `Rec1 (Either String :+: [])` or likewise for the `Rec0 [a]` in the rank-0 `Rep`. A commited parser could produce this data structure in streaming fashion, and the consumer can inspect each `Rec1` value for `L1` (parse error) or `R1` (parsing continues).

---

<div class="post-metadata">

**Author:** ![jwaldmann](https://avatars.discourse-cdn.com/v4/letter/j/977dab/32.png) [@jwaldmann](https://discourse.haskell.org/u/jwaldmann)\
**Post date:** [August 21, 2026, 12:26pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/6 "2026-08-21T12:26:25Z")

</div>

> `commit` is an interesting combinator. Is there prior art?

Function `guarantee` in Section 5.11 of Wadler: Monads for Functional Programming (1995) [https://link.springer.com/chapter/10.1007/3-540-59451-5\_2](https://link.springer.com/chapter/10.1007/3-540-59451-5_2) appears similar. It uses `head u : tail u` to assert that `u` is non-empty (non-deterministic parser gives at least one result)

NB: I was not aware of that, until you asked. It seems I just forgot, as I was at Wadler’s class at AFP 1995. I did recall the topic, so that was the place I started looking now.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [August 21, 2026, 1:14pm UTC](https://discourse.haskell.org/t/like-deriving-read-but-faster/14585/7 "2026-08-21T13:14:32Z")

</div>

In logic programming this is also called [“cut”](https://en.wikipedia.org/wiki/Cut_(logic_programming)). That is also a common example of a scoped effect in the algebraic effects literature (parsing is basically nondeterminism + state).

Edit: oh maybe that’s not quite the same because it does not guarantee success, it only stops backtracking.
