# Making public types concrete

**URL:** https://discuss.ocaml.org/t/making-public-types-concrete/16296
**Category:** Community
**Created:** [March 17, 2025, 10:07am UTC](https://discuss.ocaml.org/t/making-public-types-concrete/16296 "2025-03-17T10:07:39Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![lukstafi](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lukstafi/32/3442_2.png) [@lukstafi](https://discuss.ocaml.org/u/lukstafi)
#### Post date: [March 17, 2025, 10:07am UTC](https://discuss.ocaml.org/t/making-public-types-concrete/16296/1 "2025-03-17T10:07:40Z")

</div>

Continuing the discussion from [Upcoming Cmdliner 2.0 changes that need your attention](https://discuss.ocaml.org/t/upcoming-cmdliner-2-0-changes-that-need-your-attention/16211/2):

> [@Upcoming Cmdliner 2.0 changes that need your attention](https://discuss.ocaml.org/t/upcoming-cmdliner-2-0-changes-that-need-your-attention/16211/2):
>
> P.S. I think there’s not a single occurence where I did not eventually regret making a public type concrete.

The loss of deep pattern matching can be a pain. For example, experienced it with PrintBox – it exposes the main type as a view which only works with shallow matching.  
[printbox `type view`](https://ocaml.org/p/printbox/latest/doc/PrintBox/index.html#type-view.ext)

This is a problem specifically because of the the design of PrintBox where backends and extensions are whole separate packages. Would be fine to keep `PrintBox.t` abstract by default, but opt-in concrete for backends and extensions.

---

<div class="post-metadata">

### Author: ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)
#### Post date: [March 17, 2025, 3:20pm UTC](https://discuss.ocaml.org/t/making-public-types-concrete/16296/2 "2025-03-17T15:20:12Z")

</div>

> [@lukstafi](#):
>
> The loss of deep pattern matching can be a pain.

Right, I’m sure exceptions exists. But the thing I did in cmdliner was particularly stupid, namely in 2011 I defined:

```ocaml
type 'a conv = 'a parser * 'a formatter

```

There is no interest in pattern matching here… unless there is. While sifting through package failures I saw many cmdliner users that were reusing bits of the predefined converters so they would write:

```ocaml
let parse, _ = Arg.int32 in
…

```

Which is totally natural – I’m to blame for having exposed a pair – but provides no gain if you compare it to the _silently_ recommended way since 2017:

```ocaml
let parse = Arg.conv_parser Arg.int32 

```

The next error was likely to wait 8 more years to make it abstract, though I didn’t try to track down if people using pairs wrote the code before 2017.

But I still think that in most of my libraries I’d be pressed to find an example where exposing concrete type to “regular” users of the library makes sense. And I know a few other were I did and I will regret it, e.g. ([`Vg.Font.t`](https://erratique.ch/software/vg/doc/Vg/Font/index.html#type-t) or [`Vg.Path.outline`](https://erratique.ch/software/vg/doc/Vg/P/index.html#type-outline)).

There are also a few other cases where exposing the representation is useful, but the all the ones I can think of are for some sort of advanced usage like writing a new backend for processing a representation. In this case I think it’s better to convert with a representation type exposed in a `T.Private` or `T.Repr` module. For example [`Vgr.Private`](https://erratique.ch/software/vg/doc/Vg/Vgr/Private/index.html) or [`Jsont.Repr`](https://erratique.ch/software/jsont/doc/Jsont/Repr/index.html) or [`Htmlit.El.Low`](https://erratique.ch/software/htmlit/doc/Htmlit/El/Low/index.html) – which shows that I have been highly inconsistent in naming these modules :–)
