[−][src]Module molt::value
The Value Type
The Value
struct is the standard representation of a data value
in the Molt language. It represents a single immutable data value; the
data is reference-counted, so instances can be cloned efficiently. Its
content may be any TCL data value: e.g., a number, a list, a string, or a value of
an arbitrary type that meets certain requirements.
In TCL, "everything is a string": every value is defined by its string representation, or string rep. For example, "one two three" is the string rep of a list with three items, the strings "one", "two", and "three". A string that is a valid string rep for multiple types can be interpreted as any of those types; for example, the string "5" can be used as a string, the integer 5, or a list of one element, the value "5".
Internally, the Value
can also have a data representation
, or data rep
, that
reflects how the value has been most recently used. Once a Value
has been used
as a list, it will continue to be efficiently used as a list (until it is used something
with a different data rep).
Value is not Sync!
A Value
is associated with a particular Interp
and changes internally to optimize
performance within that Interp
. Consequently, Values
are not Sync
. Values
may be used to pass values between Interps
in the same thread (at the cost of
potential shimmering), but between threads one should pass the value's string rep instead.
Comparisons
If two Value
's are compared for equality in Rust, Rust compares their string reps;
the client may also use the two Values
as some other type before comparing them. In
TCL expressions the ==
and !=
operators compare numbers and the
eq
and ne
operators compare string reps.
Internal Representation
"Everything is a string"; thus, every Value
has a string
representation, or string rep. But for efficiency with numbers, lists,
and user-defined binary data structures, the Value
also caches a
data representation, or data rep.
A Value
can have just a string rep, just a data rep, or both.
Like the Tcl_Obj
in standard TCL, the Value
is like a two-legged stork: it
can stand one leg, the other leg, or both legs.
A client can ask the Value
for its string, which is always available
and will be computed from the data rep if it doesn't already exist. (Once
computed, the string rep never changes.) A client can also ask
the Value
for any other type it desires. If the requested data rep
is already available, it will be returned; otherwise, the Value
will
attempt to parse it from the string_rep, returning an error result on failure. The
most recent data rep is cached for later.
For example, consider the following sequence:
-
A computation yields a
Value
containing the integer 5. The data rep is aMoltInt
, and the string rep is undefined. -
The client asks for the string, and the string rep "5" is computed.
-
The client asks for the value's integer value. It is available and is returned.
-
The client asks for the value's value as a MoltList. This is possible, because the string "5" can be interpreted as a list of one element, the string "5". A new data rep is computed and saved, replacing the previous one.
With this scheme, long series of computations can be carried out efficiently using only the the data rep, incurring the parsing cost at most once, while preserving TCL's "everything is a string" semantics.
Shimmering: Converting from one data rep to another is expensive, as it involves parsing the string value. Performance can suffer if the user's code switches rapidly from one data rep to another, e.g., in a tight loop. The effect, which is known as "shimmering", can usually be avoided with a little care. Note that accessing the value's string rep doesn't cause shimmering; the string is always readily available.
Value
handles strings, integers, floating-point values, lists, and a few other things as
special cases, since they are part of the language and are so frequently used.
In addition, a Value
can also contain external types: Rust types that implement
certain traits.
External Types
Any type that implements the std::fmt::Display
, std::fmt::Debug
,
and std::str::FromStr
traits can be saved in a Value
. The struct's
Display
and FromStr
trait implementations are used to convert between
the string rep and data rep.
-
The
Display
implementation is responsible for producing the value's string rep. -
The
FromStr
implementation is responsible for producing the value's data rep from a string, and so must be able to parse theDisplay
implementation's output. -
The string rep should be chosen so as to fit in well with TCL syntax, lest confusion, quoting hell, and comedy should ensue. (You'll know it when you see it.)
Example
For example, the following code shows how to define an external type implementing a simple enum.
use molt::types::*; use std::fmt; use std::str::FromStr; #[derive(Debug, PartialEq, Copy, Clone)] pub enum Flavor { SALTY, SWEET, } impl fmt::Display for Flavor { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if *self == Flavor::SALTY { write!(f, "salty") } else { write!(f, "sweet") } } } impl FromStr for Flavor { type Err = String; fn from_str(value: &str) -> Result<Self, Self::Err> { let value = value.to_lowercase(); if value == "salty" { Ok(Flavor::SALTY) } else if value == "sweet" { Ok(Flavor::SWEET) } else { // The error message doesn't matter to Molt Err("Not a flavor string".to_string()) } } } impl Flavor { /// A convenience: retrieves the enumerated value, converting it from /// `Option<Flavor>` into `Result<Flavor,ResultCode>`. pub fn from_molt(value: &Value) -> Result<Self, ResultCode> { if let Some(x) = value.as_copy::<Flavor>() { Ok(x) } else { Err(ResultCode::Error(Value::from("Not a flavor string"))) } } }
Special Implementation Types
Values can also be interpreted as two special types, Script
and VarName
. The
Interpreter uses the (non-public) as_script
method to parse script bodies for
evaluation; generally this means that a script will get parsed only once.
Similarly, as_var_name
interprets a variable name reference as a VarName
, which
contains the variable name and, optionally, an array index. This is usually hidden
from the extension author by the var
and set_var
methods, but it is available if
publically if needed.
Structs
Value | The |