Zulip Chat Archive
Stream: lean4
Topic: Opaque type with interface
Marcus Rossel (Feb 11 2022 at 20:26):
Is it possible to define an opaque type (via constant
) about which some properties are known?
E.g. if I want to say that Value
is a type and it should be the case that x
is a value of type Value
, I would start with:
constant Value: Type _
constant x : Value
Now Lean complains: failed to synthesize Inhabited Value
Since we know nothing about Value
, there isn't any sensible way to define x
manually either.
Is there some way to make this setup work though?
Henrik Böving (Feb 11 2022 at 20:31):
Via an axiom.
Marcus Rossel (Feb 11 2022 at 20:32):
Henrik Böving said:
Via an axiom.
Mhh yeah, I was afraid that would be the only way.
Henrik Böving (Feb 11 2022 at 20:33):
Do you have a concrete example from your project? Maybe we can get that to work without an axiom
Marcus Rossel (Feb 11 2022 at 20:38):
In the spirit of #xy, the reason why I'm asking is the following:
I have a Thing
type that acts on instances of some Value
type. What the concrete Value
type is doesn't really matter though, I only need to know that it has an element x
. So currently I model Value
as a type parameter (with the appropriate type class constraint):
class ValueType (Value : Type _) where
x : Value
struct Thing (Value : Type _) [ValueType Value] where
doThing: Nat -> Value
This is kind of annoying, because throughout the entire project, I never care what Value
is, so I always have to use Thing
with an additional variable:
variable (Value : Type _) [ValueType Value]
def f (t : Thing Value) : Thing Value := ...
I'd like to somehow state once that Value
is just some type that fulfils certain requirements and that Thing
uses that type (without being specific about what Value
is).
Henrik Böving (Feb 11 2022 at 20:45):
First things first you can get rid of your ValueType
, it's equivalent to Inhabited
...but if you need access to some arbitrary value of your generic type there will be no prettier way than the Inhabited
typeclass I know of.
Marcus Rossel (Feb 11 2022 at 20:46):
Henrik Böving said:
First things first you can get rid of your
ValueType
, it's equivalent toInhabited
...but if you need access to some arbitrary value of your generic type there will be no prettier way than theInhabited
typeclass I know of.
In my real world project it's more like a pointed type. The x
is a value with a specific meaning.
Reid Barton (Feb 11 2022 at 20:49):
For your original question you can make a constant
of type "type together with an element of the type" and then unpack that into two definitions Value
and x
.
Reid Barton (Feb 11 2022 at 20:51):
I'm more confused by the longer explanation though--if you don't care what Value
is then why not just use Unit
? if you do care then why would it make sense to use constant
, or not express the dependence of Thing
on it?
Marcus Rossel (Feb 11 2022 at 20:52):
Reid Barton said:
For your original question you can make a
constant
of type "type together with an element of the type" and then unpack that into two definitionsValue
andx
.
Like this?: constant Value : Σ V : Type _, V
Marcus Rossel (Feb 11 2022 at 20:58):
Reid Barton said:
I'm more confused by the longer explanation though--if you don't care what
Value
is then why not just useUnit
? if you do care then why would it make sense to useconstant
, or not express the dependence ofThing
on it?
I don't want to use a concrete type (like Unit
) so that I don't accidentally use any properties specific to that type in my construction.
The type parameter approach does exactly what I want, it's just unwieldy.
Marcus Rossel (Feb 11 2022 at 20:59):
I think I'm going to go with this now:
private constant _Value : Σ V : Type _, V := Sigma.mk Unit Unit.unit
def Value : Type _ := _Value.fst
constant Value.absent : Value := _Value.snd
struct Thing where
doThing: Nat -> Value
Thanks for your help!
Yakov Pechersky (Feb 11 2022 at 21:19):
This looks like a job for typeclasses that have a single x record
Yakov Pechersky (Feb 11 2022 at 21:20):
You have concrete structures that are instances of that typeclass. And functions that don't care about the particular value are constrained to take in types that are instances of that typeclass
Yakov Pechersky (Feb 11 2022 at 21:20):
That typeclass is "like" declaring a scoped, namespaced axiom
Marcus Rossel (Feb 11 2022 at 21:21):
Yakov Pechersky said:
You have concrete structures that are instances of that typeclass. And functions that don't care about the particular value are constrained to take in types that are instances of that typeclass
Yes, that's what my project currently uses. But as previously mentioned, that has the annoying side effect that I can't just use Thing
, but instead always have to use Thing Value
.
(In my real project I have another type like Value
, so its even worse: Thing Value Other
)
François G. Dorais (Feb 11 2022 at 22:48):
This looks really similar to how floats are handled in core. There, Inhabited is justified by a trivial object (e.g. 0 or NaN, whatever). That's enough to make all partial definitions work. The value of the trivial object is totally irrelevant. However, as far as Lean theorem proving is concerned, all partial definitions might always return that value.
It's unusual that your Thing
has no random value, that amounts to a useless type, which is something to complain about, I guess... Your Thing
probably has some useless value that you can formalize in Lean!
François G. Dorais (Feb 11 2022 at 22:59):
From your last message, it seems that Thing
might be a discriminated type where it either applies to Foo
or Bar
depending on whether it gets a Foo or a Bar. In that case, consider formalizing the discriminant but not the implementation.
Last updated: Dec 20 2023 at 11:08 UTC