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.
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>
Any opaque type will inspect to <opaque>
by default
This never got added, but I also wanted to throw the type name in the printout
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
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:
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.
it's just about making details that might potentially be sensitive opt-in rather than opt-out
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"
ok
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.
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
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.
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
I'm wondering if we should rename "opaque" types to "secret" types...
Relatively few international English speakers know what opaque means
We've talked about better alternatives for opaque in the past, not sure if "secret" was ever suggested
Or maybe Sealed?
Taken from sealed classes a la Kotlin
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
So that makes opaque a good name, even if it's an odd word
Private also works
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.
Private sounds great!
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.
Oh, I guess a person being opaque is negative in connotation
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.
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
Yeah, I also thought of secretive types, but that doesn't sound right either
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
I like black-box types
Black-box types help you debug after your program crashed :stuck_out_tongue:
I'm sorry, are you saying "black-box types" are already a defined and known concept? A google search did not turn up anything
I was jokingly referring to these: https://en.wikipedia.org/wiki/Flight_recorder
Oooh :big_smile:
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!"
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