On 6/28/2017 2:04 PM, Don Y wrote:
> On 6/28/2017 12:56 PM, bitrex wrote:
>> On 06/28/2017 01:51 PM, Don Y wrote:
>>
>>> Exactly. You use the fetched "bytecode" as an index into a list of
>>> function pointers and invoke the function referenced. To it, you pass
>>> a pointer into the "instruction stream" so it can fetch "arguments" from
>>> the instruction stream, returning the updated "PC" for use by the next
>>> iteration of the interpreter.
>>>
>>>> You can have foolproof variant types that will happily hold both function
>>>> pointers and data in the same "package", on the same software stack, using
>>>> a custom stack allocator designed as appropriate for the capabilities of
>>>> the hardware. You can have locally scoped lambda functions generated
>>>> on-the-fly at compile time and stored in flash memory that can be templated
>>>> to accept any number of arguments from the stack as is required automatically.
>>>
>>> "Typing" is something that requires careful thought. Often, you don't *need*
>>> a variety of types: a numeric type and a string type can cover a lot of
>>> applications. More types inevitably lead to more conversions between types,
>>> formatted printing, etc. My scripting language uses one of each and allows
>>> them to "grow", as dictated by the application.
>>
>> The C++ solutions available for discriminated unions on x86 platforms are STL
>> containers like boost::variant and boost::any. boost::variant uses function
>> pointers under the hood IIRC, while boost::any uses black magic and is much
>> more generic (and about a thousand times slower.)
>>
>> <http://www.boost.org/doc/libs/1_64_0/doc/html/variant.html>
>>
>> Functions have never been first-class objects in C++ but with C++11 and some
>> template library stuff they can come pretty close. You can pass them around
>> to other functions by reference or value, hold them in containers, put them
>> on stacks and in queues, whatever.
>>
>> I think numeric types should be "strong" and suited to the application,
>> basically classes. Stuff like "int" and "float" are really too broad. Ideally
>> it should never be possible to try to assign the return value of something
>> like a temperature sensor to another type that can hold say complex numbers
>> or floating point values way outside the range of Earthlike temperatures -
>> the compiler should complain.
>
> Again, it depends on the "market" you're trying to address. I have
> ONE numeric type and one string type. I don't require the user to
> set aside buffer space for strings -- there are no buffer overruns
> (until you run out of memory and the system ABENDS.)
This is the sort of script a "user" might write (with the declarations elided
and a fair bit of hand-waving -- just to give a flavor of what a user is
likely to WANT to do and the amount of detail he DOESN'T NEED TO SEE). He'd
undoubtedly cobble it together from other scripts that *appeared* to work,
blissfully ignorant (an intentional consequence!) of all the machinations
happening behind the scenes:
wait(TOMORROW)
wait until the time-of-day indicates a date one greater than when this
initially executed (i.e., just after midnight)
(error, sun_up) := astronomical_event(here, today, SUNRISE)
if (error != nil) ...
Declare "error" to be of type "error_t" and "sun_up" to be a "time_of_day_t"
(both as defined in the prototype for "astronomical_event()" function).
Invoke the astronomical_event RPC passing our current location ("here")
and date ("today") to it while requesting the datum called "SUNRISE".
Assign any returned error code to "error" and the sought after time to
"sun_up".
Note that something else has determined where "here" happens to be (for
a home/business, it's essentially a constant; for a MOTOR HOME it could
*vary* from day to day, hour to hour! The user shouldn't have to concern
himself with those sorts of details...)
wait(sun_up)
twiddle our thumbs until the sun is expected to rise, *today* (bug if
we're too far north!)
confirmation: channel of (processID, error_code)
create a communication channel that can be used to interactively receive
data from the processes we're about to spawn -- those data to consist
of tuples containing a process identifier and an error_code
rooms := query(house_layout, "room", "exposure = east")
query the database for a list of rooms that have an eastern exposure
count := 0
while (rooms != nil) {
room := hd rooms
rooms = tl rooms
spawn open_blinds(room, confirmation)
count++
}
peal each east-facing room off the "list of rooms" and create a process
for each to open the blinds in that room. Provide each process with a
handle to the communication channel so they can report back to us! (note
that we have no idea WHERE those processes will actually execute, or
even if they will all execute on the same node; but, we *do* know that
the comm channel will magically connect each of them to us.) So, we
should expect "count" replies!
while (count > 0) {
(process, error) := <= confirmation
if (error != nil)
print("OhMiGosh! " process " reported " error)
count--
}
wait for replies from all of these spawned processes (they will terminate
when they've performed their desired actions) -- note that we have no idea
what order the processes will post their replies -- nor do we care!
(error, sun_down) := astronomical_event(here, today, SUNSET)
if (error != nil) ...
end_day := sun_down - 15 * MINUTES
wait(end_day)
we'll close the blinds *before* the sun actually sets so it doesn't come
streaming in the west-facing windows as it falls low in the sky. A
smarter routine would take into account the extent of the overhang on
that side of the house and query when the sun would fall below a certain
elevation -- based on our location and the current date -- that would
cause the sun's rays to come through those windows!
so, similarly issue another query to identify WEST facing rooms and spawn
processes to CLOSE the blinds in those rooms at end_day, harvesting all
replies by reusing that communication channel (whose endpoint is fixed,
HERE), etc.
Note that the user doesn't have to be concerned with whether we have
0, 1, 5, or 8-gazillion rooms in the house. Nor does he care about the
length of the list returned from these queries. Nor the lengths of
the strings used to define each "room". Nor the format of the "here" or
"today" 'constants'. He doesn't care that print may not be wired to a
"console" or any other interface that the user is likely to see -- that's
something else's "problem".
Note that the language infers the types of variables from the asignments
made to them (e.g., because "confirmation" is defined as a "channel of
(processID, error_code)", the types of the "(process, error)" tuple
are infered from the assignment *from* that channel. This helps reduce
the "declaratory clutter" in most programs.
The syntax is unfortunate. But, I've not been able to come up with a
cleaner way of expressing the same constructs -- without adding arbitrary
function names (like "read_channel()" instead of the "<=" operator).
Likewise, the error handling is something that a user would rather not
have to deal with -- nor is he likely to handle it all well! An
alternative is to catch exceptions but, to the user, that just looks like
a glorified ABEND! :<
E.g., astronomical_event() could return obvious errors like "invalid place",
"invalid date", "unknown event", etc. But, as its an RPC/IPC, it could
also return more esoteric errors like "service not available", "insufficient
privilege for requested operation", "timeout", etc. There are just some
things that you can't elide and the user has to expect that they *might*
occur (else his code fails for unspecified reasons).
If *I* was writing this (and was at a facility with scads of windows, etc.),
I'd prefer to use a cursor to parse the results of the queries "server side"
rather than risking pulling some glob of data into my local address space
just to nibble away at a SECOND COPY of it (the first residing on the DBMS
server!).
Or, I might issue a query to count the number of records satisfying the
query's constraints *before* actually issuing the query:
"Wow! 18,932 windows??! Something is wonky! I sure don't want to
set up 18,932 processes -- even if I parse the queries server-side to
avoid that overhead -- as that will undoubtedly exceed my resource
quota leaving me wondering why the program crashed before it had a
chance to get started!"
So, let the user be inefficient and hope he improves (or is helped to improve)
without forcing him to understand the mechanisms and costs of his actions.