Skip to content

docs: add page about opaque types - #1246

Draft
jderochervlk wants to merge 1 commit into
masterfrom
vlk/opaque-types-documentation
Draft

jderochervlk wants to merge 1 commit into
masterfrom
vlk/opaque-types-documentation

Conversation

@jderochervlk

@jderochervlk jderochervlk commented Apr 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #543 and #508

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare deployment

Deployement ID: f8a06913-13b5-4e68-a148-cf516fbfd334
Deployment Environment: preview

⛅️ wrangler 4.63.0 (update available 4.81.1)
─────────────────────────────────────────────
✨ Compiled Worker successfully
Uploading... (7840/7842)
Uploading... (7841/7842)
Uploading... (7842/7842)
✨ Success! Uploaded 2 files (7840 already uploaded) (1.44 sec)

✨ Uploading _redirects
✨ Uploading Functions bundle
🌎 Deploying...
✨ Deployment complete! Take a peek over at https://f8a06913.rescript-lang.pages.dev
✨ Deployment alias URL: https://vlk-opaque-types-documentati.rescript-lang.pages.dev

@jderochervlk

Copy link
Copy Markdown
Collaborator Author

I'm not sure we want to call them "opaque", "distinct", or "branded" types.

@jderochervlk

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T13:37:21.178443Z 2773cdd Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2773cdd6da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


Let's start with out email example using an imaginary `EmailApi` that sends a message to an email address we provide it.

```res

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add validation markers to the new code blocks

Every code fence on this page is plain res, so scripts/test-examples.mjs skips the entire page because it only parses res example, res prelude, and res sig blocks. As a result, the invalid calls on lines 16, 22, and 65 pass yarn test; mark the intended-valid snippets with the supported fence types and provide any required prelude so documentation examples remain compiler-checked.

AGENTS.md reference: AGENTS.md:L195-L195

Useful? React with 👍 / 👎.

We would get failures is we tried to send a message to an invalid email address.

```res
sendEmail("not_an_email**123")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Supply the message in the invalid-email call

This call cannot demonstrate a runtime failure for an invalid address because the preceding sendEmail function takes two unlabelled arguments, while this invocation supplies only one. ReScript 12 rejects it for incorrect arity before EmailAPI.send can run, so pass a message argument here.

Useful? React with 👍 / 👎.

let sendEmail: (t, string) => unit
} = {
type t = string
let sendEmail = (email: t, message: t) => EmailAPI.send((email :> string), string)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the message value instead of the string type

The refactored implementation passes string as the second argument, but that is a type name rather than a value; ReScript reports that the value string cannot be found. It also unnecessarily annotates message as the email type t; use message: string and pass message to EmailAPI.send, and make the equivalent value correction in the earlier example on line 16.

Useful? React with 👍 / 👎.

---

The `:>` operator may be used to convert a polymorphic variant to a `string` or `int`, or convert an [object](../docs/manual/object.mdx) to a type with a subset of its fields.
The `:>` operator may be used to convert a polymorphic variant to a `string` or `int`, to downcast an [opaque type](../docs/manual/opaque-types.mdx) to it's primitive type, or convert an [object](../docs/manual/object.mdx) to a type with a subset of its fields.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit opaque coercion to private manifest types

This new description is false for the page's primary type t form: outside a module that exports an abstract type t, ReScript 12 rejects (value :> string) because the representation is unknown. The coercion is available when the interface exposes type t = private string; distinguish that private manifest form instead of promising that any opaque type can be downcast.

AGENTS.md reference: AGENTS.md:L59-L59

Useful? React with 👍 / 👎.

}
```

`:>` is the type coercion operator It cannot be used to change a type to a different primitive value, such as `(42 :> string)`, but we can use it to allow our opaque type to be passed to functions that work on the primitive type that it's based on. Once this is done, the value is now longer the opaque type and it will become the type we coerced it to.n We can always use an opaque type as a primitive type, but we cannot use a primitive type as an opaque type.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Repair the garbled coercion explanation

The central explanation says the coerced expression is “now longer” opaque and ends the next clause with the stray text to.n, which reverses and obscures the intended statement. Change this to “no longer the opaque type” and remove the stray character so readers can understand what type the coerced expression has.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explain opaque types

1 participant