http://www.newton-inc.com/dev/techinfo/qa/qa.htmmyEncloser := {
importantSlot: 42,
GetImportantSlot := func()
return importantSlot,
nestedSlot := {
myInternalValue: 99,
getTheValue := func()
begin
local foo;
foo := :GetImportantSlot(); // WON'T WORK; can't find function
foo := myEncloser:GetImportantSlot(); // MAY WORK
importantSlot := 12; // WON'T WORK; will create new slot in nestedSlot
myEncloser.importantSlot := 12; // MAY WORK
end
}
};
myEncloser.nestedSlot:GetTheValue();
The proper way to accomplish this is to give the nested frame a
_parent or _proto slot that references the enclosing
frame. Nesting the frame is not strictly necessary in this case, only the _proto or
_parent references are used.MyFrame:= {}; theSlotName := "Slot_1";
MyFrame.Slot_1
Intern takes a
string and returns a symbol. There is also a mechanism called path
expressions (see the NewtonScript Reference), that allows you to specify an expression or variable
to evaluate, in order to get the slot name. You can use these things
to access the slots you want: MyFrame := {x: 4};
theXSlotString := "x" ;
MyFrame.(Intern(theXSlotString)) := 6
theSlotName := "Slot_1";
MyFrame.(Intern(theSlotName)) := 7;
// myFrame is now {x: 6, Slot_1: 7} call func()
begin
local s,v;
local root := GetRoot();
local base := root.|YourApp:YourSIG|; // name of app
local prot := base._proto;
foreach s,v in base do
begin
if v and v <> root AND v <> base AND v <> prot then
begin
Write ("Slot:" && s & ", Value: ");
Print(v);
end;
end;
end with ()
The debugging function TrueSize can also be a valuable tool to
determine the heap used by your applications. See the NTK User Guide
for more information about TrueSize. thrower: func(x) begin
if x then
throw('|evt.ex.msg;my.exception|, "Some error occurred");
end;
returner: func(x) begin
if x then
return -1; // some random error code,
0; // nil, true, whatever.
end;
Code to throw and and handle an exception: local s;
for i := 1 to kIterations do
try
call thrower with (nil);
onexception |evt.ex.msg;my.exception| do
s := CurrentException().data.message;
local result;
local s;
for i := 1 to kIterations do
if (result := call returner with (nil)) < 0 then
s := ErrorMessageTable[-result];
local
s;
try
for i := 1 to kIterations do
call thrower with (nil);
onexception |evt.ex.msg;my.exception| do
s := CurrentException().data.message;
TRUE
instead of NIL so the "error" occurs every time was interesting. The return
value loop takes about 60 ticks, mostly due to the time needed to look
up the error message. The exception loop takes a whopping 850 ticks, mostly because of the overhead
in the CurrentException() call.breakOnThrows global
to stop your code and look at why there's a problem. With result
codes you have a tougher time setting break points. With a good debugger it could be argued that you
can set conditional break points on the "check the return value"
code, but even when you do this you'll have lost the stack frame of the function that actually had
the problem. With exceptions and breakOnThrows, all
the local context at the time the exception occurred is still available for you to look at, which is
an immense aid.PrimClassOf
will return an object's primitive type.String. A string object contains a
12-byte header plus the Unicode strings plus a null termination
character. Note that Unicode characters are two-byte values. Here's an example:
"Hello World!"
This string contains 12 characters, in other words it has 24 bytes. In
addition we have a null termination character (24 + 2 bytes) and an
object header (24 + 2 + 12 bytes), all in all the object is 38 bytes big. Note that we have not
taken into account any possible savings if the string was compressed
(using the NTK compression flags).kInkChar is used to mark the position of an ink
word. The ink data is stored after the null termination
character. Ink size varies depending on stroke complexity. [12, $a, "Hello World!", "foo"]
We have a header (12 bytes) plus four bytes per element (12 + (4 * 4) bytes).
The integer and character are immediates, so no additional space
is used, but we have 2 string objects that we refer to, so the total is (12 + (4*4) + 38 + 20
bytes) 86 bytes. We have not taken into account savings concerning
compression. Note that the string objects could be referred by other arrays and frames as well, so
the 38 and 20 byte structures are stored only once per
package. {Slot1: 42, Slot2: "hello"}
We have a header of 28 bytes, and in addition we have two slots, for a total of
(28 + (2 * 8)) 48 bytes. This does not take into account the space
used for each of the slot name symbols or for the string object. (The integer is an immediate, and
so is stored in the array.)
{Slot1: 56, Slot2: "world"}
We have a header of 12 bytes. In addition, we have two slots (2 * 4), and
additional 16 bytes for the size of a map with no slots „ all in all,
36 bytes. We should also take into account the shared map, which is 16 bytes, plus the space for the
two symbols.RelBounds to create
the viewBounds frame, and it means there will be a
single viewBounds frame map in the part produced.if value.path = '|name.first| then ... // WRONG
A: There are several concerns. '|name.first| is not a path
expression, it is a symbol with an escaped period. A proper path
expression is either 'name.first or [pathExpr: 'name, 'first]. The
vertical bars escape everything between them to be a single NewtonScript
symbol.value.path = 'name.first will always fail, because path
expressions are deep objects (essentially arrays) the equal comparison
will compare references rather than contents. You will have to write your own code to deeply
compare path expressions.'name = [pathExpr: 'name] will always fail, as the
objects are different.'name.first:if
ClassOf(value.path) = 'pathExpr and Length(value.path) = 2
and value.path[0] = 'name and value.path[1] = 'first then ...
...
local myFunc := func(...) ...;
local futureSoupEntries := Array(10, nil);
for i := 0 to 9 do
futureSoupEntries[i] := {
someSlots: ...,
aFunction: myFunc,
};
...
A: When a function is defined within another function, the lexically enclosing
scope (locals and paramaters) and message context (self) are "closed
over" into the function body. When NewtonScript searches for a variable to match a symbol in a
function, it first searches the local scope, then any lexically
enclosing scopes, then the message context (self), then the _proto and _parent chains from the
message context, then finally the global variables.TotalClone is
made during the process of adding an entry to a soup, and this
includes the function body, lexical scopes, and message context bound up within any functions in the
frame. All this can take up a lot of space.DefConst('kMyFunc, func(...) ...))
it will not have the lexically enclosing scope, and the message
context at compile time is defined to be an empty frame, and so cloning such a function will take
less space. You can use the constant kMyFunc within the
initializer for the frame, and each frame will still reference the same function body.
(Additionally, the symbol kMyFunc will not be included in the
package, since it is only needed at compile time.)_proto based scheme instead. Each soup entry will necessarily contain a complete copy
of the function, but if you can guarantee that the function body
will always be available within your application's package, it might be unnecessarily redundant to
store a copy with each soup entry.TrueSize to get the size of a soup entry I get
results like 24K or even 40K for the size. That can't be right. What's
going on?TrueSize "knows" about the underlying implementation of soup
entries. A soup entry is really a special object (a fault block) that
contains information about how to get an entry and can contain a cached entry frame. In the
information about how to get an entry, there is a reference to the soup,
and various caches in a soup contain references to the cursors, the store, and other (large)
NewtonScript objects. TrueSize is reporting the space taken
up by all of these objects. (Note: calling TrueSize on a soup entry will force the
entry to be faulted in, even if it was not previously taking up space
in the NewtonScript heap.)TrueSize is not very useful
when trying to find out how much space the cached frame for an entry
is using. A good way to find the space used for a cached entry frame is to call gc();
stats(); record the result, then call
EntryUndoChanges(entry); gc(); stats(). The difference between the two free space
reports will be the space used by the cached frame for a given
entry. EntryUndoChanges(entry) will cause any cached frame to be removed and
the entry to return to the unfaulted state. Gc() then collects
the space previouly used by the cached entry frame.TrueSize
breakdown of the types of objects used, you can Clone
the entry and call TrueSize on the copy. This works because the copy is not a
fault block, and so it does not reference the
soups/cursors/stores.Floor and Ceiling seem broken. For
instance, Floor(12.2900 * 10000) returns 122899, not
122900. What's going on? Floor or
Ceiling. This happens because of the way floating point numbers are
stored, and the limitation is common to many real number representations. In the same way that 1/3
cannot accurately be represented in a finite number of digits in
base 10 (it is .3333333333...), likewise 1/10 cannot be exactly represented as a fractional part in
base 2. Because number printers typically round to a small number
of significant digits, you don't normally notice this. The NTK inspector, for example, displays
only 5 significant figures in floating point numbers. However, if you
display the number with enough precision, you'll see the representation error, where the real is
actually slightly larger or smaller than the intended value.
FormattedNumberStr(0.1, "%.18f") -> "0.100000000000000010"
FormattedNumberStr(0.3, "%.18f") ->
"0.299999999999999990"
Floor and
Ceiling are strict, and do not attempt to take this error into
account. In the example, 12.29 is actually 12.2899999999999990, which
multiplied by 10000 is 122,899.999999999990.
The largest integer less than this number (Floor) is correctly
122899.RIntToL, which rounds to the nearest integer
avoiding the problems caused with round-off error and Floor or Ceiling.
RIntToL(x) produces the same result that
Floor(Round(x)) would produce. RIntToL(12.29*10000) ->
122900
FormattedNumberStr. These functions typically round
to the nearest displayable value. To display 2 decimal digits, use
"%.2f": FormattedNumberStr(12.29, "%.2f") -> "12.29"
$29.95 as the integer 2995 or 29950, then divide by
100 or 1000 to display the number. If you do
this, keep in mind that there is a maximum representable integer value, 0x1FFFFFFF or
536870911, which is sufficient to track over 5 million
dollars as pennies, but can't go much over that.Floor deals with round off errors, you'll need to do some extra work keeping track of
the precision of the number and the magnitude of the round off
error. It's worthwhile to read a good numeric methods reference. Floating point numbers in
NewtonScript are represented by IEEE 64-bit reals, which are accurate to
around 15 decimal digits. The function NextAfterD provides a handy way to see how
'close together' floating point numbers are.
FormattedNumberStr(NextAfterD(0.3, kInfinity), "%.18f");
-> "0.300000000000000040"
http://devworld.apple.com/dev/techsupport/insidemac/PPCNumerics/PPCNumerics-2.html
The Newton floating point environment is not as rich in features
as the PowerPC environment, and the PowerPC numerics document is only mentioned as a useful resource
for understanding floating point issues. It in no way documents
API or features of the Newton floating point environment.1001.1100001 1.0011100001 * 2^3 3+0x3FF = 0x402 = 100 0000 0010 0 10000000010
0011100001000000000000000000000000000000000000000000 0x4023840000000000StrHexDump(9+97/128, 16) -> "4023840000000000"real. In addition to the
NewtonScript floating point literal syntax, you can use the compile time function
MakeBinaryFromHex to construct real numbers, and you must use this style
for custom NaN values. The most recent platform files for Newton 2.0 and Newton 2.1 provide
constants for negative zero (kNegativeZero), positive and
negative infinity (kInfinity, kNegativeInfinity), and a canonical NaN
(kNaN). MakeBinaryFromHex("4023840000000000", 'real) -> 9.7578125 // = 9+97/128