pub struct Value(/* private fields */);Expand description
A Cove value.
An abstract type. What it holds is private to this crate: a host
builds one through a constructor, reads one through a reader, and
classifies one through Value::view. ADR 0028 decision 6 is where that
was decided and issue #196 is where it was asked. Every change to what a
value is had been a source break for the hosts that matched on it, and
the record is not that pub variants are survivable but that each change
was individually rescued — twice by luck and once by a hand-written
compatibility shim.
The one thing sealing takes away is the compile error a host got when a
new variant arrived, and ValueView gives that back deliberately and
exhaustively. What it does not give back is a compile error when the
runtime moves a value from a Box to an Rc, which is the whole point:
those two were the same error before, and they are unrelated events.
Implementations§
Source§impl Value
The builtin Option, Result, and Error values, built and read through
the one description of what they are made of.
impl Value
The builtin Option, Result, and Error values, built and read through
the one description of what they are made of.
Ok, Err, Some, None, and an Error’s message are declared in
cove_schema::builtins, which is also where cove-sema reads them to
check a match and to type a pattern’s binding. Everything in this
workspace that builds one of these values or asks which case a value is
goes through the constructors and readers below, so the four case names
are stated once and the question “is this an Ok?” has one answer.
Sourcepub fn structure<N: Into<Rc<str>>>(
type_name: impl Into<Rc<str>>,
fields: impl IntoIterator<Item = (N, Value)>,
) -> Value
pub fn structure<N: Into<Rc<str>>>( type_name: impl Into<Rc<str>>, fields: impl IntoIterator<Item = (N, Value)>, ) -> Value
A value of the declared struct type type_name, carrying fields in
declaration order.
type_name is the qualified name the declaring module gives it, such
as rules.policy.PullRequest: that is the name every value of a
declared type carries, and the name an invocation and the Host API
boundary both check against.
This exists so that a host building an argument for
Vm::invoke does not have to name
StructValue’s layout to do it — the Rc, the field vector, and in
particular opaque, which records that the declaration said export opaque struct (ADR 0014) and is therefore not a thing a caller has an
answer for. Issue #109 asks that the internal representation become
less exposed to embedders; this is one place it was exposed for no
reason.
Sourcepub fn enumeration(
type_name: impl Into<Rc<str>>,
case: impl Into<Rc<str>>,
payload: impl IntoIterator<Item = Value>,
) -> Value
pub fn enumeration( type_name: impl Into<Rc<str>>, case: impl Into<Rc<str>>, payload: impl IntoIterator<Item = Value>, ) -> Value
A value of the declared enum type type_name, in the case case,
carrying payload in the order the case declares it.
The companion of Value::structure for the other declared shape.
Value::ok and the three beside it build the builtin enums, whose
case names come from cove_schema::builtins and are not a caller’s to
choose; this one takes both names because a package’s own enum is a
package’s own.
Sourcepub fn array(items: impl IntoIterator<Item = Value>) -> Value
pub fn array(items: impl IntoIterator<Item = Value>) -> Value
An Array holding items, in order.
The companion of Value::structure, and there for the same reason: a
host that builds an array should not have to know that the elements are
stored behind a shared pointer to a slice.
Sourcepub fn set(items: impl IntoIterator<Item = MapKey>) -> Value
pub fn set(items: impl IntoIterator<Item = MapKey>) -> Value
A Set holding items.
The elements are MapKeys and not Values, and that is the
MapKey restriction showing through rather than an inconvenience: a
set of values would be a constructor that could fail, and there is
nothing sensible for it to do when it does. A host building one from
its own data writes the key it means — MapKey::Str(name) for a set of
names — and a host holding a Value it did not build converts with
MapKey::from_value, which reports the part that cannot be a key and
the path to reach it.
Duplicates collapse, exactly as they do for a Set a Cove program
builds, and the order the set iterates in is ascending key order
whatever order they arrived in.
Sourcepub fn map(entries: impl IntoIterator<Item = (MapKey, Value)>) -> Value
pub fn map(entries: impl IntoIterator<Item = (MapKey, Value)>) -> Value
A Map holding entries.
The companion of Value::set, with the same reason for taking a
MapKey: only the key carries the restriction, so the value half is
an ordinary Value. A later entry under a key an earlier one used
replaces it.
Sourcepub fn int(n: i64) -> Value
pub fn int(n: i64) -> Value
The Int n.
A full sixty-four bits, because an Int is one: issue #109 measured
the alternatives that are not, and NaN boxing and pointer tagging are
refused rather than deferred because neither can hold every Int and
every Float at once.
Sourcepub fn duration(nanos: i64) -> Value
pub fn duration(nanos: i64) -> Value
The Duration of nanos nanoseconds.
Nanoseconds rather than a std::time::Duration, for the reason
Value::as_duration_nanos gives on the way out: a Cove duration is
a signed count of them, and -1s is an ordinary value that
std::time::Duration cannot hold.
Sourcepub fn string(text: impl Into<Rc<str>>) -> Value
pub fn string(text: impl Into<Rc<str>>) -> Value
The String text.
Named for the Cove type and not for Rust’s, which is why it takes
anything a string can be made from rather than a String
specifically: Value::string("hi") copies the characters once and
says nothing about where they end up.
Sourcepub fn range_of(start: i64, end: i64, inclusive_end: bool) -> Value
pub fn range_of(start: i64, end: i64, inclusive_end: bool) -> Value
The range start..end, or start..<end when inclusive_end is
false.
Both bounds as source writes them, rather than the normalised
half-open pair Value::range answers with: 1..3 and 1..<4 cover
the same integers and are still two different values, since ==
compares the bounds a range was written with.
The name is range_of and not range because the reader took
range, and the readers are what issue #195 shipped.
Sourcepub fn from_resource(handle: impl Into<Arc<ResourceHandle>>) -> Value
pub fn from_resource(handle: impl Into<Arc<ResourceHandle>>) -> Value
A handle to a resource the host owns, such as a database connection.
The companion of Value::resource, and it takes the whole handle
because ADR 0013 decides that a handle is a name and every field of
it is part of that name. What it hides is the shared pointer, which is
there so a handle can cross into a task when its schema allows it —
pass either a ResourceHandle or the Arc that
ResourceHandle::new answers.
The name is from_resource and not resource because the reader took
resource.
Sourcepub fn host_fn(module: impl Into<Rc<str>>, op: impl Into<Rc<str>>) -> Value
pub fn host_fn(module: impl Into<Rc<str>>, op: impl Into<Rc<str>>) -> Value
A bound host operation, such as console.println.
The companion of Value::host_op. Two names and not an
implementation: what they name is found in the registry at the call.
Sourcepub fn type_value(name: impl Into<Rc<str>>) -> Value
pub fn type_value(name: impl Into<Rc<str>>) -> Value
A type used as a value, such as Vector in Vector.of(1, 2).
The name is type_value and not type_name because
Value::type_name answers the name of the type a value is, which
is a different question asked of every value rather than of this one.
Sourcepub fn ok_payload(&self) -> Option<&[Value]>
pub fn ok_payload(&self) -> Option<&[Value]>
What an Ok carries, when this is one.
The payload is a slice rather than a value because what a caller does
with an empty one differs: the ? operator answers () and a
diagnostic answers nothing at all. The schema says an Ok carries
exactly one value, so an empty one is a host that broke its word.
Sourcepub fn err_payload(&self) -> Option<&[Value]>
pub fn err_payload(&self) -> Option<&[Value]>
What an Err carries, when this is one.
Sourcepub fn some_payload(&self) -> Option<&[Value]>
pub fn some_payload(&self) -> Option<&[Value]>
What a Some carries, when this is one.
Sourcepub fn error_message(&self) -> Option<&Value>
pub fn error_message(&self) -> Option<&Value>
The message a builtin Error carries, when this is one.
Sourcepub fn same_type_as(&self, other: &Value) -> bool
pub fn same_type_as(&self, other: &Value) -> bool
The name shown in diagnostics.
Whether other is a value of the same type as this one, without
naming either type.
== has to refuse a comparison between two types before it compares
two values, and it asked that question by building both type names and
comparing the strings. Two allocations per comparison is a great deal
to pay for an answer that is a discriminant check and, for the two
declared kinds, one string comparison — and a parser compares
characters constantly, so this was measurable (issue #104). The names
are still built for the diagnostic, which happens once.
Sourcepub fn declared_type_name(&self) -> Option<&Rc<str>>
pub fn declared_type_name(&self) -> Option<&Rc<str>>
The name of the declared type this value is of, for a struct or an
enum, and None for everything else.
A method declared in a package can only ever be found on one of those
two, so this is what receiver dispatch asks rather than
Value::type_name: no name is built at all for the receivers that
have none, and a declared one hands back the name it already holds.
A Some here is not a declared type. The builtin Option and
Result are Repr::Enum as well, and answer their own bare names —
Option, with no module in front. A caller asking “is this a
declared type” has to look for the dot; a caller that read is_some
as the answer got two builtins wrong.
pub fn type_name(&self) -> String
Sourcepub fn erased(&self) -> &Value
pub fn erased(&self) -> &Value
The value a trait object holds, or this value when it is not one.
A dyn Trait wrapper records where a value was converted, and the
checker decides where that is: a written type converts and a lambda’s
inferred result does not, though both have type dyn Trait. Nothing
a program can ask should be able to tell those two apart, so
everything that compares, renders, or keys a value looks through the
wrapper first.
Source§impl Value
Reading a value without naming its representation.
impl Value
Reading a value without naming its representation.
The constructors above — Value::structure, Value::enumeration,
Value::array, Value::set, Value::map, and the builtin four —
let a host build every shape that crosses the boundary without writing
an Rc, a Box, a field vector or the opaque flag. These are the other
half: they let a host read the same shapes the same way.
Only one half existed, and the missing half cost something every time the
representation moved. Issue #104 made Value::Struct an
Rc<StructValue>; issue #109 put Value::HostFn’s two names behind one
pointer and took every value in the program from forty bytes to
twenty-four; issue #121 replaced a closure’s parameter list with an arity;
issue #183 replaced an enum payload’s Vec<Value> with Payload. Every
one of those was invisible to a host that only built values and a source
break for one that read them, because reading meant matching on a variant
and a match on a variant is a match on the representation. Issue #186 is
where that was written down, and this is its answer.
A reader borrows. It hands back a reference into the value it was
asked about and the caller clones what it means to keep, which is what
Value::ok_payload and StructValue::get already did and what a host
wants: a conversion into the host’s own types reads each part once and
keeps no Value at all. Borrowing is the half of this that constrains
what can still move, and it constrains it in one direction — every part a
reader answers with has to be stored as the thing it answers with. A
struct’s fields can move behind a different pointer, a shared shape table,
or an inline arity the way Payload already did, and none of that is
visible here; they cannot become values that are computed — unpacked
from a tagged word, decoded lazily, or held under a lock — without these
signatures changing. A reader that cloned would forbid none of that, and
would charge every read for the possibility. A Vector is where
the line already falls, and it falls the same way for building: its
elements are behind a RefCell because the language lets an alias write
them, so there is no borrowing reader for one here and no constructor for
one above.
A wrong shape answers None. Asking an Int for its fields is the
host’s mistake rather than the program’s — no Cove code asked for it and
none can handle it — so it is not a
RuntimeError; and it is not a panic
either, because a host converting a value it did not build wants to report
what arrived instead. Value::type_name is what names that in the
report. This is the convention the readers that already existed use:
Value::ok_payload, Value::error_message and StructValue::get
all answer None to the question they were not the right value for.
A reader looks through dyn Trait. Each of these calls
Value::erased first, for the reason that method gives: the wrapper
records where a value was converted, nothing a program can ask should be
able to tell a written dyn Trait from a lambda’s inferred one, and
fmt::Display already looks through it — “the wrapper is a
representation, not something the program put there”. There is no reader
for the wrapper itself, which matches the constructors, none of which can
build one.
Sourcepub fn as_int(&self) -> Option<i64>
pub fn as_int(&self) -> Option<i64>
The Int this is.
A full sixty-four bits, because an Int is one and overflow is a
broken invariant rather than a wrap.
Sourcepub fn as_duration_nanos(&self) -> Option<i64>
pub fn as_duration_nanos(&self) -> Option<i64>
The Duration this is, in nanoseconds.
Nanoseconds rather than a std::time::Duration, because a Cove
duration is a signed count of them: -1s is an ordinary value and
std::time::Duration cannot hold it.
Sourcepub fn declared_type(&self) -> Option<&str>
pub fn declared_type(&self) -> Option<&str>
The declared type this value is of — rules.policy.Decision, or
Option for a builtin — for a struct or an enum, and None for
everything else.
The qualified name Value::structure and Value::enumeration
take, which is what a host checks an answer against before reading it
apart. Value::declared_type_name answers the same question with
the shared handle itself, because the two backends clone it to
dispatch a method; this is the reader, and it does not say what the
handle is made of.
Sourcepub fn field(&self, name: &str) -> Option<&Value>
pub fn field(&self, name: &str) -> Option<&Value>
The field name of a struct value.
None both when this is not a struct and when the struct declares no
such field, because a host has the same thing to say about either and
Value::type_name is what says it: “Int carries no field
policy” and “rules.policy.Decision carries no field polciy” are
the same sentence with the name filled in.
Sourcepub fn fields(&self) -> Option<impl Iterator<Item = (&str, &Value)> + '_>
pub fn fields(&self) -> Option<impl Iterator<Item = (&str, &Value)> + '_>
A struct value’s fields, in declaration order, and None when this is
not a struct — which is also how a host asks whether it is one.
Declaration order rather than the order anything asked for: it is the
order Value::structure was handed and the order the declaration
states, so a host reading a struct positionally reads what a host
building one wrote.
An export opaque struct (ADR 0014) answers here like any other. The
flag governs rendering, because a Display has no idea which module
is watching; a host holding the value has already been handed it, and
hiding the fields from it would hide them from the very code the
module exported the value to.
Sourcepub fn case(&self) -> Option<&str>
pub fn case(&self) -> Option<&str>
The case of an enum value, such as Some, Err, or Require.
The case alone, unqualified, exactly as Value::enumeration takes
it; Value::declared_type is the other half of the name.
Sourcepub fn payload(&self) -> Option<&[Value]>
pub fn payload(&self) -> Option<&[Value]>
What an enum value’s case carries, in the order the case declares it.
A slice, and an empty one for a case that carries nothing, for the
reason Value::ok_payload gives: what a caller does with an empty
payload differs and only the caller knows which. Those four ask a
builtin question — “is this an Ok?” — and answer the payload as a
consequence; this one is asked of a value whose case the caller reads
for itself with Value::case, which is what a package’s own enum
needs.
Sourcepub fn items(&self) -> Option<&[Value]>
pub fn items(&self) -> Option<&[Value]>
An Array’s elements, in order.
The companion of Value::array. A Vector answers None and that
is not an oversight: its elements are behind a RefCell because an
alias may write them, so nothing can hand out a plain slice of them,
and there is no constructor for one either.
Value::vector_elements is how a vector is read — a guard rather
than a slice, which is what a part behind a cell can answer with.
Sourcepub fn elements(&self) -> Option<impl Iterator<Item = &MapKey> + '_>
pub fn elements(&self) -> Option<impl Iterator<Item = &MapKey> + '_>
A Set’s elements, in ascending key order.
MapKeys and not Values, for the reason Value::set gives on
the way in: the restriction is real, and showing it is better than a
reader that pretends a set holds anything. MapKey::to_value
converts one back.
Ascending key order whatever order they were inserted in, which is the order a Cove program iterating the same set sees.
Sourcepub fn entries(&self) -> Option<impl Iterator<Item = (&MapKey, &Value)> + '_>
pub fn entries(&self) -> Option<impl Iterator<Item = (&MapKey, &Value)> + '_>
A Map’s entries, in ascending key order.
The companion of Value::map, with the same split: only the key
carries the MapKey restriction, so the value half is an ordinary
Value.
Sourcepub fn range(&self) -> Option<RangeBounds>
pub fn range(&self) -> Option<RangeBounds>
A Range’s bounds, half-open.
RangeBounds rather than the three fields the variant holds,
because .. and ..< are two ways of writing one range: 1..3 and
1..<4 cover the same integers, and a host asking what a range covers
should not have to normalise them itself. The bounds are i128 so
that an inclusive i64::MAX end cannot overflow.
Sourcepub fn resource(&self) -> Option<&ResourceHandle>
pub fn resource(&self) -> Option<&ResourceHandle>
The resource handle this is.
ResourceHandle is the answer rather than something this hides,
because ADR 0013 decides that a handle is a name: “every field of it
is part of the name”, and there is no field for state because the
state is the host’s. What this hides is the Arc, which is there so
that a handle can cross into a task when its schema allows it.
Sourcepub fn host_op(&self) -> Option<(&str, &str)>
pub fn host_op(&self) -> Option<(&str, &str)>
The module and operation a bound host operation names, such as
("console", "println").
Two names and not an implementation: a bound host operation is a name
the way ValueView::HostModule is, and what it names is found in the
registry at the call. This is the reader for the variant issue #109
boxed to buy the sixteen bytes — a host that matched Value::HostFn { module, op } had to be rewritten, and one that had called this would
not have noticed.
Sourcepub fn arity(&self) -> Option<usize>
pub fn arity(&self) -> Option<usize>
How many parameters a closure value declares.
Parameters and not arguments a call must supply: a defaulted or a
variadic parameter counts like any other. A host that was handed a
callback asks this to refuse one of the wrong shape before calling it
back through Reentry, which is the only way
to call one, since the body belongs to the backend that made it. This
is the reader for the field issue #121 replaced with a count, and it
is the whole of a closure a host has any business reading.
Sourcepub fn vector_elements(&self) -> Option<Elements<'_>>
pub fn vector_elements(&self) -> Option<Elements<'_>>
A Vector’s elements, in order, for as long as the guard is held.
The companion of Value::items, which answers None for a
Vector because its elements sit behind a cell — an alias may write
them, so nothing can hand out a plain &[Value] of them. Issue #196
records that as “the one place the borrow-based reader design cannot
reach”; ADR 0028 decision 7 is where it is reached, and this is the
shape of the answer: a part whose storage will not sit still is handed
out as an opaque guard, and the guard is public API.
Elements derefs to [Value], so a host reads a vector the way it
reads an array. Holding one borrows the vector: drop it before
calling back into Cove through
Reentry, because Cove code that writes the
same vector while the guard is alive is a panic rather than a data
race.
Sourcepub fn dyn_trait(&self) -> Option<&str>
pub fn dyn_trait(&self) -> Option<&str>
The trait a dyn Trait value was used at, such as render.Display.
The one reader that does not look through the wrapper, because it is
the reader for the wrapper. Every other reader and
Value::view call Value::erased first, for the reason that
method gives — the wrapper is a representation, not something the
program put there — so this is how a host that genuinely wants to name
the trait in a diagnostic asks for it.
Sourcepub fn view(&self) -> ValueView<'_>
pub fn view(&self) -> ValueView<'_>
Classify this value: what kind of Cove value it is, and its parts.
O(1), allocates nothing, and borrows from self. It looks through
dyn Trait exactly as every reader beside it does, which is why
ValueView has no Dyn variant; Value::dyn_trait is how a host
asks about the wrapper.
This is the exhaustive match that sealing takes away, given back
deliberately. See ValueView for what it promises and when it
breaks.
A Vector borrows its elements for as long as the view is held, for
the reason Value::vector_elements gives.
Trait Implementations§
Source§impl Debug for Value
Value prints as the value it is, and not as a wrapper around one.
impl Debug for Value
Value prints as the value it is, and not as a wrapper around one.
Forwarded rather than derived so that the newtype the seal is made of
leaves no trace in a rendering: a Debug of an Int is Int(3), exactly
as it was when Value was the enum itself. Tests, traces and diagnostics
all read this, and none of them should have to know.