|
Special properties of the 'set key
John Cowan
(12 Aug 2020 12:50 UTC)
|
|
Re: Special properties of the 'set key
hga@xxxxxx
(12 Aug 2020 13:18 UTC)
|
|
Re: Special properties of the 'set key
Lassi Kortela
(13 Aug 2020 06:58 UTC)
|
|
Re: Special properties of the 'set key
hga@xxxxxx
(13 Aug 2020 10:55 UTC)
|
|
Re: Special properties of the 'set key Lassi Kortela (14 Aug 2020 07:37 UTC)
|
|
Sub-set key and zero/nonzero success/failure status sets
Lassi Kortela
(14 Aug 2020 08:47 UTC)
|
|
Re: Special properties of the 'set key
John Cowan
(14 Aug 2020 04:28 UTC)
|
|
Re: Special properties of the 'set key
Lassi Kortela
(14 Aug 2020 08:20 UTC)
|
> set/probably no longer facility is unique in "dividing the universe of
> statuses."
Dividing the universe of status codes, yes. Other, unrelated properties
also divide the universe of status objects (e.g. 'severity divides it
into different severities; statuses from different codesets can have the
same severity it's a useful division even without knowing the set).
This illustrates the principle that the status code is not the be-all,
end-all thing about a status object. Sometimes you just want to display
the message and it doesn't matter how the object is classified.
> I'm anticipating this corner of the Schemerepository will
> use sets to divide conventions, very possibly one file per set.
Schemeregistry? Sounds like a good idea. There are probably also
set-independent properties that are useful to have; they could go into a
separate list.
> Note it's the *only* mandatory property, it's entirely legal to make a
> status or raise an error using only it, as I do in a couple of examples.
The thing is, an error with only 'message or 'code is more useful than
an error with only 'set :) 'message is something you can display as is.
'code at least lets you infer success or failure if you can make an
educated guess as to what API the sender was using internally, even if
the sender neglected to mention that API in the status object.
'set by itself just tells you that something errno-related happened.
Note that errno 0 means success. So you're told that either success or
error happened somewhere, but you don't know anything else.
This kind of thing always happens when spec writers try to make rules
anticipating everyone's use cases :) We're in classic "spec writer's
disease" territory right now.
> BTW, it's occurred to me that we should add a 'sub-set key. Maybe for
> 'sqlstate as you and John have explored, where SQL RDBMSes that follow
> it would naturally have that as the 'set, and e.g. 'postgresql as a
> subset. Just checked and it even covers connection failures.
>
> And I've identified what I'm currently naming "generic-unix-lib" for
> libraries like libsodium that return only a sentinel value like -1 on
> error, 0 on success. It has no structure beyond that, except of
> course for what's naturally covered by the standard keys of
> scheme-procedure, foreign-interface, and message.
>
>> My intuition says we're mandating too many things for programmers who'll
>> be facing situations we don't know. We should mandate the bare minimum.
>> The bare minimum is all properties optional with #f defaults.
>
> But at least one properly, whatever it is?
>
>> If a system can return only the error message, that's better than
>> nothing. Absent properties are the same as #f, so (eq? 'errno
>> (foreign-status-ref st 'set)) can still be used to test it even if it's
>> #f. A set of #f means unknown, but other useful information like the
>> message can still be known.
>
> I can see scheme-procedure and message, for example, or as you say,
> even message alone providing useful error reporting....
>
> So while we have to require least one key (?), you say we shouldn't
> enforce our will by insisting all uses of SRFI 198 be neatly
> subdivided into sets...?
>
> That's a legitimate argument, I don't think your intuition is broken.
We totally should permit status objects with no properties at all.
Here we should take a page from mathematicians and their fondness for
zero, which works wonders to enable simple and predictable systems in
programming as well. If you type (+) into a Lisp system (i.e. the add
operator with zero arguments) it dutifully returns 0 which is the
identity for addition. (*) returns 1 which is the identity for
multiplication. So the zero case does something useful. Likewise, as
Schemers we've learned that we should always write our recursive
procedures such that the base case does something useful.
As it happens, all the Lisp/Scheme collection representations (plist,
alist, hash-table) also work fine with zero keys. In principle, a status
object is just a collection tagged with a 'foreign-status tag. Hence I
think it should have an API similar to the hash-table API or John's
upcoming dictionary API (but more compact).
>> (eq? 'errno (cadr (foreign-status-plist st))) is definitely not a good
>> idiom. Not only is "cadr" the kind of obfuscation they don't have in
>> other languages like Python, but implicitly relying on the concrete
>> structure of the plist is more obfuscation on top.
>
> Presumably if you really care about this will check for 'set, or
> whatever you expect to be there using foreign-status-ref, I am
> requiring status objects be an abstract type. Extracting every key
> and value so a programmer, especially an end user, can do whatever
> he needs with the whole "set" of them except for mutating the list is
> the objective of foreign-status-plist. Otherwise he has to call
> foreign-status-keys to get them as a list and then iterate over that
> with foreign-status-ref. Very possibly creating his own plist,
> alist, whatever.
We can have `foreign-status-fold` instead of keys if that's better. It
doesn't really matter IMHO.
> We think this is a useful utility which does indeed destroy the
> abstraction, especially if 'set is no longer required.
It's equally useful as any other enumeration API; if I understood
correctly, John's concern was that it's faster to return the whole list.
> I guess John
> has failed to sufficiently communicate to you the virtues of
> foreign-status-plist. He for example said:
>
>> I continue to prefer foreign-status-plist. For one thing, it means
>> that if you have searched for the key you want, you get the value in
>> O(1) time. If the internals of the object actually are a plist
>> (which is a reasonable implementation strategy), it takes O(n^2)
>> operations to get all the key-value pairs.
Big-O complexity is not an issue here. Realistically, objects will have
max 10 fields and will only be enumerated by interactive systems that
want to display them.
Normal code will simply reach for `foreign-status-ref` to get at the
specific properties it wants, which will be O(1) or O(n) depending on
the internal representation.
>> Encapsulation is fine when there is something to encapsulate, but
>> when the object is immutable there's no real reason for it. We can
>> say that it is an error to modify the list returned by -plist so
>> that it can can be handed out in O(1) time if the internal format is
>> also a plist.
My overarching concern is that our object and its API are lacking
coherence. We take in a concrete representation (plist), turn it into an
abstract datatype, then add a procedure to give back the same concrete
representation, except it may be reconstructed from a different internal
representation.
We're doing indirection, not abstraction. We're not (meaningfully)
abstracting over the plist, we're just tagging it.
If we insist on a concrete representation, we could simply tell people
to use a plist tagged by a symbol at the front:
(cons 'foreign-status the-plist-i-want)
That's one valid worldview, but it doesn't really need a SRFI.
In my experience, opaque datatypes are easier to handle and to evolve
over time to meet new requirements. That's why I like that we have a
SRFI that specifies an abstract datatype. I'd like to maintain the
abstraction. Without the abstraction there's not much point to having
the status object.
TBH Emacs is on my mind. RMS insisted that concrete representations are
better than opaque ones because they are more hackable or something. In
my experience that's just wrong. Having a Lisp system show you concrete
representations for its internals is really confusing because then you
have to understand those internals. The whole point of a big (Lisp)
system with proper abstraction is that you're not expected to understand
everything and can't accidentally break stuff by mangling concrete
representations you're not familiar with. XEmacs and Common Lisp have
proper opaque datatypes and that stuff is way nicer to work with.
When you ask FemtoLisp to display a procedure, it shows you stuff like this:
> string->list
#fn("7000r1c0q]41;" [#fn("8000r1c0qm02|c1~31_42;"
[#fn(":000r2|`W640};c0i10|32m02~|c1i10|32}K42;" [#fn(string.dec)
#fn(string.char)]) #fn(sizeof)])] string->list)
That's not good. It's a neat hack that the concrete representation of a
procedure is an ASCII-based bytecode, but as a user who just wants to
call it to turn a string into a list I'm confused.
Opaque datatypes are a key enabler of basic sanity when dealing with big
systems.
> Back to you:
>
>> As a separate issue, the concrete representation of a plist doesn't have
>> standard tools in R6RS or R7RS-small. If we return a list it should be
>> an alist.
>
> plists are very, very simple. Without an SRFI for them, I just say:
>
>> This SRFI uses property lists (plists). They are simple lists of
>> alternating keys and values, such as:
>>
>> ('key1 "value1" 'key2 "value2")
>
> Alists introduce messiness when the value is a list, although that's
> not the end of the world. But you have a point about their not being
> supported in the standards, and without an SRFI, each user creating
> statues and errors, and consumer of foreign-status-plist will have to
> come up with his own routines. Although we could either suggest some
> very simple ones, maybe include their code in the SRFI (???) without
> making them a real standard???
There are two fundamentally different issues here: what we take from
users, and what we return to users.
We can take arbitrarily complex data representations from users, since
the code to parse those representations is internal to the SRFI's
implementation. Users don't see it. It's no trouble at all to take
plists even in systems that don't have plist utilities.
But when we return data to users, they will have to bring their own
tools to work with it, as you say. As SRFI implementors we don't mind
writing our own plist iterator, but we don't want the users of our API
to have to write anything other than simple one-liners.