|
Variables, Data Types, Declaring Variables, Comments, and Database Access |
Welcome to the fundamentals of Sheerpower! In this tutorial, you'll learn how to store information in variables, understand the different types of data you can use, and how to document your code with comments. Along the way, you'll also see how Sheerpower's naming conventions and type system keep programs clear and easy to read.
Variables are containers for storing data. In Sheerpower, a
variable name must begin with a letter and can contain letters,
numbers, and underscores. Variable names are case-insensitive and
typically use underscores to improve readability, like
tax_rate or full_name$.
Throw-away identifiers: Sheerpower also reserves two special throw-away identifiers:
_ for numbers and _$ for strings.
Assign to them when you want an expression to run and want a visual
signal that you are intentionally ignoring its return value.
This makes your intent clear at a glance.
For example, the join() function quickly joins
strings together and returns the final length. If we don't need
the length, we can do this:
Every variable holds a specific type of data. Sheerpower's core data types are:
For text.
"Hello!"
a$ = %text
Sally
Tom
Mary
%end text
For numbers.
123.454.123e50 (Scientific Notation)
For true/false.
TRUE
Inherits the data type of the last value assigned. Rarely used.
Rarely used. Use REAL instead. Sheerpower's REAL type handles whole numbers perfectly, making a separate INTEGER type unnecessary for most applications.
42
dynamic data types?
Variables can be created either automatically (by using type suffixes)
or explicitly (with the declare statement).
Sheerpower supports both styles, so you can choose the one that
fits your code and team conventions.
$ for a STRING (e.g., name$)? for a BOOLEAN (e.g., found?) — read it as a question% for an INTEGER (e.g., count%)# for a REAL, stated explicitly (e.g., rate#)
— the same type as no suffix, but visible. It matters most on
routine parameters: a bare parameter name adapts to whatever the
caller passes, while routine area_of with width#, height#,
returning area# says every one of them is a real.
x, x#, x?, x% and x$
are all different variables.declare): Can improve clarity,
organizes declarations, and reduces errors. This is especially helpful
in larger programs or shared codebases.
OPTION REQUIRE DECLARE to enforce explicit
declarations. The compiler will raise an error if a variable
is used without being declared first.
declare
if you like the clarity they provide (for example, declare boolean found?).
Sheerpower's REAL type is designed for exact decimal
arithmetic. Unlike IEEE floating-point types used in most languages,
REAL values deliver 16 digits of true decimal precision,
avoiding the rounding errors that plague floating-point math.
This makes REAL ideal for financial, accounting,
and other applications where accuracy is critical.
REAL is stored as multiple 64-bit integers:
some for the integer part (IP) and one for the fractional
part (FP), scaled by 1016.
For example, 13.599 is internally represented as:
This design eliminates floating-point approximation and enables
efficient comparisons and arithmetic.
Expressions like (0.1 + 0.2) - 0.3 evaluate to
0 in Sheerpower.
By contrast, the same expression in IEEE math yields:
0.00000000000000005551115123125783
For statistics and large-scale data, Sheerpower supports scientific
notation (e.g., 4.123e50). The REAL type provides
a high-precision decimal numeric type supporting up to
54 digits before and
16 digits after the decimal point. Within this range,
decimal values are represented exactly, so calculations are
predictable and do not introduce binary floating-point rounding error.
When greater dynamic range or additional decimal precision is required, REAL automatically uses scientific notation while retaining up to 64 significant decimal digits. The exponent can have a magnitude of up to 1,000,000,000 (a one followed by a billion zeros), allowing REAL values to represent extremely large or extremely small numbers.
Fractions, Complex Numbers, and the Two Specials:
The same REAL holds three more kinds of value, with no
declaration and no second type. A divide of two whole numbers whose
quotient does not terminate keeps the exact fraction:
a = 1 / 3 is one third, not a rounded decimal, so
a * 3 is exactly 1, and it prints as the
decimal you expect (.3333333333333333) until you ask for the
fraction with fraction$(a). See Exact Fractions: the RATIONAL Mode.
A complex number is written with the i suffix:
z = 3 + 4i. Arithmetic, abs(z) (which is 5 here),
sqr(), exp(), log() and the trig
functions all take one, and a negative square root that raises an
error on a plain real works once you say you mean the complex plane:
sqr(-4 + 0i) is 0+2i. See Processing Complex Numbers.
Finally, a REAL can hold NaN and
Inf. They are never produced by ordinary arithmetic on
finite values (1 / 0 still raises), but they come in through
data — a val("NaN"), a CSV cell, a float field of a
table — and propagate the IEEE way: x = _inf then
x - x is NaN, and isnan(),
isinf() and isfinite() find them.
a = 1 / 3 print a * 3 ! 1 print fraction$(a) ! 1/3 z = 3 + 4i print abs(z) ! 5 print sqr(-4 + 0i) ! 0+2i print typeof$(a) ! Name:A, Dtype:Real, *Rational* print typeof$(z) ! Name:Z, Dtype:Real, *Complex*
typeof$() names the mode a value is in
(*Narrow*, *Wide*, *Sci*,
*Rational*, *Complex*, *NaN*,
*Inf*); the engine moves between them by value, and
programs never have to.
(0.1 + 0.2) - 0.3
Sheerpower does not convert between types on your behalf. A STRING
value cannot be assigned to a REAL variable, even when it looks
numeric—"123.45" is still text, not a number. Assigning it
directly is a compile-time error; use VAL() to convert it
explicitly. DYNAMIC variables (documented above) are the one exception.
Sheerpower lets you define your own constants and custom data types. These features make code more readable, expressive, and secure.
A const is fixed at compile time. A variable can change
forever. Configuration data lives in the gap between them: it is
loaded at runtime — from a file, a logical, a table — and
from that moment on it should never change again.
Freezable variables close that gap. Load the value normally, then
freeze it. From then on, any attempt to change it raises
a clear error instead of silently corrupting your configuration.
Problem: Configuration values are loaded once and trusted everywhere. But nothing stops a stray assignment — a scratch variable that happens to share the name, a routine writing where it shouldn't — from silently changing them mid-run. The symptom appears far from the cause and is miserable to track down.
Solution: Declare the variable freezable, load
it, then freeze it. Any later write raises a catchable
error naming the variable, and the value is untouched.
Efficiency: The frozen check rides the engine's existing assignment path — reads cost nothing, and writes cost one flag test. Freezing is virtually free insurance.
Takeaway: If a value should never change after loading, freeze it. The bug you would have spent an afternoon hunting becomes a one-line error message at the exact statement that tried it.
Only variables declared freezable can be frozen. This is
the gate: the variable's author decides whether freezing is part of
its contract, and no other code can freeze a variable that was never
meant to be frozen.
Cluster members may be declared freezable too, so individual fields of a configuration cluster can be protected.
freeze and unfreeze are runtime statements
— a variable is frozen from the moment the freeze
statement executes, not from where it appears in the source. A
variable may be assigned freely before freezing, including many times
during a loading loop.
The error is catchable with when exception, and the
frozen value is untouched. Every way of writing a variable is
covered — plain assignment, retries++,
join() into a frozen string, lset,
substring assignment (frozen$[1:2] = "ab"), a routine's
returning value, and even writing through a
view into a frozen string.
typeof$() reports the state at any time:
unfreeze re-opens a frozen variable for writing. Use it
to create a small, deliberate window for changes — such as
reloading a configuration file — and refreeze immediately:
Keep the unfreeze and the matching freeze
in the same routine, and keep the window small. A useful property of
this habit: searching your source for unfreeze finds
every place a frozen value can legitimately change. Anything else
raises an exception.
permafreeze freezes a variable permanently for the rest
of the run. It cannot be thawed — unfreeze on a
permafrozen variable raises an error rather than quietly reopening it.
The distinction between the two is worth stating plainly:
freeze protects against accident — any
code could still unfreeze it deliberately.
permafreeze protects against intent —
once set, nothing in the program can change the value for the rest of
the run. Use freeze for values that may legitimately
reload, and permafreeze for load-once values that must
survive the whole run untouched.
The things most often loaded at runtime and never meant to change are
not single values but tables: a list of tax bands, the day names, a
lookup cluster read from a file at startup. Those can be frozen as a
whole. Declare the array or cluster freezable after the
dim or cluster statement, and freeze it once
it is loaded.
A frozen array is frozen in contents and shape: an
element write, an append with (*), any fill,
redim, or an array-expression assignment raises the same
exception a frozen variable does, and the array is untouched. Reading
it, size(), the statistics functions, and passing it to a
routine as a with values(*) parameter are all free. The
freeze travels with the data: a routine that receives the array as a
returning parameter raises on its own write.
A frozen cluster is frozen in contents and row set: a
field write on any row, add cluster, delete from
cluster, reset cluster, cluster input
and copy cluster into it all raise. Everything that only
looks stays free — collect with its
include and sort by, for each,
findrow, set cluster: row n. One rule worth
stating: a store inside a collect block, such as
words->count = _extracted, is a field write and raises
— that is exactly the quiet mutation freezing exists to catch.
A cluster handed to a routine raises in the routine too. Freezing a
member and freezing the cluster are independent: unfreezing the
cluster leaves a member you froze separately still frozen.
permafreeze works on both, with the same one-way rule.
typeof$ reports all of it: typeof$(rates) gives
Name:RATES, Dtype:Real, *Array*, Dims:1, Bounds:1:3, Size:3 ,
*Fixed*, *Freezable*, *Frozen*, and typeof$(cities)
gives Name:CITIES, *Cluster*, Rows:2, *Freezable*, *Frozen*.
"") or zero. Occasionally useful for "this
feature stays off for this run."
show all report the frozen state, so a
"why won't this assign?" question answers itself in the diagnostics.
The professional pattern: combine freezing with
private routine style. Globals are accessed explicitly
with the main$ prefix, so every access is visible and
searchable — and freezing makes those globals immutable after
loading. Configuration that is explicit, auditable, and
untouchable: load it, freeze it, and trust it for the rest of
the run.
freezable after the dim or
cluster statement; contents and shape (or row set)
become immutable, reads and collect stay free.
declare freezable is the gate — only declared
variables can be frozen; anything else is a compile error.
freeze and unfreeze are runtime
statements — assign freely while loading, then freeze.
permafreeze is permanent for the run —
unfreeze on it raises. Freeze guards against
accident; permafreeze guards against intent.
unfreeze then
finds every legitimate change site.
typeof$(), crash dumps, and show all
all report the frozen state.
This makes code more self-explanatory—using money
signals financial data, not just a number. Custom data types are
enforced at compile time, as the next example shows.
The compiler replies:
If the program crashes, password appears as
<nodump> in debug output—helping to protect secrets
from accidental exposure.
nodump modifier ensures those
values are hidden, showing <nodump> instead of
the actual contents.nodump for secrets or private
data to keep applications safe without extra coding effort.
$ — Literal dollar sign%.2m — Two decimal places in monetary format
Sheerpower provides a secure way to access table fields using
table_name(field_name). This separates code from data
and protects against SQL injection.
Database access is an advanced feature. To learn more, see the Integrated Database Access Overview tutorial.
Comments are ignored by the compiler and help others understand your code.
Use // (preferred) or ! to begin a comment.
Sheerpower enforces variable types. You cannot assign a STRING directly
to a REAL or vice versa — use conversion functions.
For a complete walkthrough, see Strings to Reals -- Extracting Numbers From Text.
Conversions like val() and str$() are
commonly used when reading user input, importing raw data, or
constructing strings for display.
OPTION REQUIRE DECLAREnodump)declare format! and //)val() and str$()$, ?, %) or be created with declare.REAL is the default numeric type with exact 16-digit precision.OPTION REQUIRE DECLARE) enforces explicit declarations.val() to convert strings to numeric REAL values.str$() to convert numbers/booleans to strings.const) cannot be changed after declaration.money or nodump).declare format sets the default printed output style for a variable.table(field) syntax._ and _$ to discard results intentionally.! or // for meaningful comments that explain why, not just what.|
Hide Description
|
|
|
Enter or modify the code below, and then click on RUN |
|
Looking for the full power of Sheerpower?
Check out the Sheerpower website. Free to download. Free to use. |