Stream: API design

Topic: `Secret a` type in stdlib?


view this post on Zulip Jasper Woudenberg (Oct 23 2024 at 17:33):

A type I've defined in Haskell in the past is this:

Secret a := a

wrap : a -> Secret a
wrap = \x -> @Secret x

unwrap : Secret a -> a
unwrap = \@Secret x -> x

This is made useful by adding ability implementations so that printing Secret (through Inspect, Encoding, or any other way) always ensures in something like <secret> rather than whatever value is contained within.

The goal here is to keep sensitive information wrapped as it gets passed around an application, and only unwrapped right before the sensitive value is used somewhere. To prevent accidents like the secret ending up in logs.

I was wondering if it'd be a good idea to include a type like this in the standard library. That might help create a convention of libraries producing or consuming sensitive values automatically wrapping them in this fashion. Besides the proposed crypto module I don't know if the Roc stdlib itself has much use for a Secret type. But if other libraries want to communicate secrets between them it could be useful, like an environment configuration parser producing Secret-wrapped values and a databse library consuming a Secret-wrapped credential.

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 17:54):

I think this more or less is builtin:

app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }

import pf.Stdout

Secret a := a

main =
    @Secret "this is my secret"
    |> Inspect.toStr
    |> Stdout.line!

will print:

<opaque>

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 17:55):

Any opaque type will inspect to <opaque> by default

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 17:55):

This never got added, but I also wanted to throw the type name in the printout

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 17:56):

I would assume that we shouldn't support it in encode. I think for most use cases it is probably better to get a type error when trying to encode an opaque type then to get a string like "<opaque>" in the encoded output

view this post on Zulip Richard Feldman (Oct 23 2024 at 17:58):

Brendan Hansknecht said:

This never got added, but I also wanted to throw the type name in the printout

I actually personally prefer not even leaking the type by default :big_smile:

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 18:00):

Hmm, I'm not really sure the tradeoffs there. It is just for debug printing. But I guess if you call an effect in a library, someone could malicious could record the type names. But I'm not really sure what that gets them.

view this post on Zulip Richard Feldman (Oct 23 2024 at 18:01):

it's just about making details that might potentially be sensitive opt-in rather than opt-out

view this post on Zulip Richard Feldman (Oct 23 2024 at 18:03):

it means that if you don't opt into Inspect, you have total peace of mind that no information is being accidentally leaked beyond "this is some opaque type"

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 18:04):

ok

view this post on Zulip Isaac Van Doren (Oct 23 2024 at 18:07):

I was wondering if it'd be a good idea to include a type like this in the standard library.

A downside of including it in the stdlib is that then users wouldn’t be able to implement any other abilities used by their applications. But having libraries that can use the same Secret type does sound convenient.

view this post on Zulip Brendan Hansknecht (Oct 23 2024 at 18:51):

But having libraries that can use the same Secret type does sound convenient.

Just have to pass in the functionality via module params. Also, probably is better to allow unwrapping specific secrets in a library rather than allow unwrapping every single secret that using the builtin Secret

view this post on Zulip Jasper Woudenberg (Oct 23 2024 at 19:53):

I think it depends a bit on what we would like to protect against.

If I'm writing a module and I have some sensitive information I don't want other code to be able to read, then I can define my own Secret-equivalent type and not expose the unwrap function.

The only reason I can imagine for putting Secret in the standard library specifically is to make exchanging sensitive data between different codebases (libraries/platform/app) safer, and restricting reading of Secret by libraries makes less sense to me.

view this post on Zulip Luke Boswell (Oct 23 2024 at 22:12):

Another option to consider is to have a package with a Secret type in it, and see if it gets a lot of usage. That will give us the data to validate the need for something like this in the builtins.

Also for future reference we've got an example which shows people how to make an opaque type with a custom Inspect impl https://www.roc-lang.org/examples/CustomInspect/README.html

view this post on Zulip Anton (Oct 25 2024 at 09:12):

I'm wondering if we should rename "opaque" types to "secret" types...

view this post on Zulip Anton (Oct 25 2024 at 09:13):

Relatively few international English speakers know what opaque means

view this post on Zulip Anton (Oct 25 2024 at 09:13):

We've talked about better alternatives for opaque in the past, not sure if "secret" was ever suggested

view this post on Zulip Sam Mohr (Oct 25 2024 at 09:20):

Or maybe Sealed?

view this post on Zulip Sam Mohr (Oct 25 2024 at 09:20):

Taken from sealed classes a la Kotlin

view this post on Zulip Sam Mohr (Oct 25 2024 at 09:21):

The problem with "secret" IMO is that it implies you don't know that the type even exists, but you can see that it exists, just not what's inside of it

view this post on Zulip Sam Mohr (Oct 25 2024 at 09:21):

So that makes opaque a good name, even if it's an odd word

view this post on Zulip Sam Mohr (Oct 25 2024 at 09:21):

Private also works

view this post on Zulip Aurélien Geron (Oct 25 2024 at 09:42):

I really like private, it's used in many languages for this purpose (hiding the implementation details), so many people will be familiar with it. Opaque is not well know and it has a negative connotation (to me at least), something that isn't clear.

view this post on Zulip Isaac Van Doren (Oct 25 2024 at 12:18):

Private sounds great!

view this post on Zulip Brendan Hansknecht (Oct 25 2024 at 16:04):

Opaque has a negative connotation....intriguing :thinking:

The main thing I think of is an opaque window (often used for privacy). So curious of the negative connotation that is somehow attached.

view this post on Zulip Brendan Hansknecht (Oct 25 2024 at 16:05):

Oh, I guess a person being opaque is negative in connotation

view this post on Zulip Brendan Hansknecht (Oct 25 2024 at 16:06):

Personally I think opaque is the most definitionally correct, but that may not be useful.

The type is not secret/private, anyone can use it if it is exposed. The data in the type is private. That said, the type is opaque. Though you can use the type, you can't see any of the data it contains.

view this post on Zulip Isaac Van Doren (Oct 25 2024 at 16:30):

That’s a good point about the type itself not being private. They could be called “privacy types” to get around that, but I’m not much of a fan of that name

view this post on Zulip Brendan Hansknecht (Oct 25 2024 at 16:36):

Yeah, I also thought of secretive types, but that doesn't sound right either

view this post on Zulip Anton (Oct 25 2024 at 16:48):

Some suggestions from o1-preview:

Hidden Types
Encapsulated Types
Black-Box Types
Concealed Types
Private Types
Abstract Data Types
Hidden Implementation Types
Interface-Only Types
Secure Types
Invisible Types

view this post on Zulip Anton (Oct 25 2024 at 16:49):

I like black-box types

view this post on Zulip Jasper Woudenberg (Oct 25 2024 at 16:50):

Black-box types help you debug after your program crashed :stuck_out_tongue:

view this post on Zulip Anton (Oct 25 2024 at 17:20):

I'm sorry, are you saying "black-box types" are already a defined and known concept? A google search did not turn up anything

view this post on Zulip Jasper Woudenberg (Oct 25 2024 at 17:43):

I was jokingly referring to these: https://en.wikipedia.org/wiki/Flight_recorder

view this post on Zulip Anton (Oct 25 2024 at 17:47):

Oooh :big_smile:

view this post on Zulip Aurélien Geron (Oct 26 2024 at 01:16):

I hadn't thought of the fact that the type itself isn't private, that's a very good point.

The word "opaque" is frequently used figuratively, almost always negatively: e.g., to describe an unclear explanation or argument, a potentially fraudulent accounting report, or a document full of jargon, etc. Of course the word can also be neutral when describing a material.

For what it's worth, my first reaction when reading "opaque type" was that it must be an advanced topic, and I delayed learning it. When I finally learned it, I thought: "oh, that's actually really easy and useful!"

view this post on Zulip Aurélien Geron (Oct 26 2024 at 01:33):

Maybe solid type (in the sense that you can't break it into its constituent parts). Or maybe boundary type (in the sense that it's the boundary between the API and the implementation).


Last updated: Jul 06 2025 at 12:14 UTC