# Persist 1.0.0.0 released

**URL:** <https://discourse.haskell.org/t/persist-1-0-0-0-released/12489>\
**Category:** Announcements\
**Created:** [July 16, 2025, 9:38pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489 "2025-07-16T21:38:27Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![iteratee](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/iteratee/32/4332_2.png) [@iteratee](https://discourse.haskell.org/u/iteratee)\
**Post date:** [July 16, 2025, 9:38pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/1 "2025-07-16T21:38:28Z")

</div>

> **[persist](https://hackage.haskell.org/package/persist-1.0.0.0)**
>
> Minimal serialization library with focus on performance

Persist is a binary serialization library with two aims

1. Have a default serialization format that can simply be derived for most types using `Generic`
2. Allow a type to have a custom serialization format that matches an external specification.

Most of the improvements with this release were for goal 2. There is now support for backpatching a length value and avoid a 2nd data-structure walk. This is useful with tag-length-value formats where the length is a fixed number of bytes. It is also a source for some performance improvements with the default format for the builtin list datatype. There is additional support for de-serializing these formats as well with the new `getPrefix` `unsafeGetPrefix` helpers.

The API is similar enough to `binary` and `cereal` that it should be easy enough to try out the library.

---

<div class="post-metadata">

**Author:** ![Ambrose](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ambrose/32/5672_2.png) [@Ambrose](https://discourse.haskell.org/u/Ambrose)\
**Post date:** [July 16, 2025, 10:09pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/2 "2025-07-16T22:09:17Z")

</div>

> `Generic`

inb4 compile time fearmonger pearl clutching (lmaooo)

---

<div class="post-metadata">

**Author:** ![Ambrose](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ambrose/32/5672_2.png) [@Ambrose](https://discourse.haskell.org/u/Ambrose)\
**Post date:** [July 16, 2025, 10:10pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/3 "2025-07-16T22:10:48Z")

</div>

On a serious note tho

> Includes utilities to match externally specified data formats.

I couldn’t find this in the module haddocks. What does this mean exactly? Could I use this library to handle a binary format like [UBJSON](https://en.m.wikipedia.org/wiki/UBJSON)? or is that not what “externally specified” means here?

---

<div class="post-metadata">

**Author:** ![iteratee](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/iteratee/32/4332_2.png) [@iteratee](https://discourse.haskell.org/u/iteratee)\
**Post date:** [July 16, 2025, 11:32pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/4 "2025-07-16T23:32:49Z")

</div>

> [@Ambrose](#):
>
> What does this mean exactly? Could I use this library to handle a binary format like [UBJSON](https://en.m.wikipedia.org/wiki/UBJSON)? or is that not what “externally specified” means here?

That is the kind of thing meant by “externally specified.” I intend to use these new features in the `cql` library to match the cassandra encoding.

For example, all of the numbers in UBJSON are written in big-endian format. You would want to use `putBE` for them. The default is little-endian if you use the default via `Generic`.

The API bits I would consider useful for external formats are:

- `putLE`/`putBE`
- `reserveSize`/`resolveSize`\*
- `getPrefix`

There is currently no lookahead or backtracking, so depending on the binary format it may be difficult to make it fit. Lookahead would be relatively easy to implement with the exposed internals.

---

<div class="post-metadata">

**Author:** ![Iceland\_jack](https://avatars.discourse-cdn.com/v4/letter/i/e68b1a/32.png) [@Iceland\_jack](https://discourse.haskell.org/u/Iceland_jack)\
**Post date:** [July 17, 2025, 9:45am UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/5 "2025-07-17T09:45:29Z")

</div>

Can you add instances for [Generically](https://hackage.haskell.org/package/base-4.21.0.0/docs/GHC-Generics.html#t:Generically) ([Programming & Proving by Jan van Brügge](https://jvanbruegge.github.io/blog/2025/please-use-generically/)).

---

<div class="post-metadata">

**Author:** ![iteratee](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/iteratee/32/4332_2.png) [@iteratee](https://discourse.haskell.org/u/iteratee)\
**Post date:** [July 17, 2025, 6:39pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/6 "2025-07-17T18:39:40Z")

</div>

The defaults rely on `Generic`, but can be easily overridden. If there were a PR that explained why `Generically` was useful, I’d be happy to merge and re-release.

---

<div class="post-metadata">

**Author:** ![jackdk](https://avatars.discourse-cdn.com/v4/letter/j/9f8e36/32.png) [@jackdk](https://discourse.haskell.org/u/jackdk)\
**Post date:** [July 21, 2025, 1:01am UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/7 "2025-07-21T01:01:09Z")

</div>

@Iceland_jack 's linked article makes a compelling case, IMHO:

> In my opinion there are two issues here: 1) the reliance on `DeriveAnyClass` which introduces a footgun for the user and 2) `DefaultSignatures` force the author to provide only one way to derive instances

It also means that you can do things like using `Generically` as the newtype for “construct instances in the obvious way, by taking advantage of the structure of the type” but if you (for example) introduce newtypes for big-endian or little-endian encodings, it makes the overall library surface more ergonomically uniform.

Also, have you considered using an indexed monad to track the number of open holes that the user needs to go back and patch? With `-XQualifiedDo` this is much more ergonomic than it used to be, and you can then prevent the user from running a putter unless the putting code goes back and fills out all the space that it reserves.

---

<div class="post-metadata">

**Author:** ![iteratee](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/iteratee/32/4332_2.png) [@iteratee](https://discourse.haskell.org/u/iteratee)\
**Post date:** [July 21, 2025, 11:32pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/8 "2025-07-21T23:32:07Z")

</div>

I’ll consider using `Generically` when I drop support for GHC 9.2

---

<div class="post-metadata">

**Author:** ![iteratee](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/iteratee/32/4332_2.png) [@iteratee](https://discourse.haskell.org/u/iteratee)\
**Post date:** [July 21, 2025, 11:37pm UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/9 "2025-07-21T23:37:36Z")

</div>

> [@jackdk](#):
>
> Also, have you considered using an indexed monad to track the number of open holes that the user needs to go back and patch? With `-XQualifiedDo` this is much more ergonomic than it used to be, and you can then prevent the user from running a putter unless the putting code goes back and fills out all the space that it reserves.

Indexed Monads or linear types would be useful to track this. Even without the guard rails it’s useful, and it’s unlikely to cause a problem. The code ends up looking like this:

```Haskell
  put l = do
    sizeHandle <- reserveSize @Word64
    go sizeHandle 0 l
   where
    go sizeHandle !n [] = resolveSizeLE sizeHandle n
    go sizeHandle !n (x : rest) = put x >> go sizeHandle (n + 1) rest

```

or something even simpler:

```Haskell

putWithLength x = do
  sizeHandle <- reserveSize @Word32
  putWithoutLength x
  resolveSizeExclusiveLE sizeHandle

```

---

<div class="post-metadata">

**Author:** ![jackdk](https://avatars.discourse-cdn.com/v4/letter/j/9f8e36/32.png) [@jackdk](https://discourse.haskell.org/u/jackdk)\
**Post date:** [July 22, 2025, 12:27am UTC](https://discourse.haskell.org/t/persist-1-0-0-0-released/12489/10 "2025-07-22T00:27:51Z")

</div>

> [@iteratee](#):
>
> I’ll consider using `Generically` when I drop support for GHC 9.2

You can use package [`generically`](https://hackage.haskell.org/package/generically) to get a compatibility shim (which re-exports the `newtype` from `base` when available, so it’s safe to unconditionally depend upon).

> [@iteratee](#):
>
> Indexed Monads or linear types would be useful to track this.

Good call on linear types - I had forgotten about them but they’d be much easier to use and probably more performant to compile than the typelevel gymnastics needed to track it all in an indexed monad. And if you’re only targeting GHC \>=9.2, you should have workable linear types available to you.
