The Forum for Discussion about The Third Manifesto and Related Matters

Please or Register to create posts and topics.

Good language design? { I rather think not }

Page 1 of 2Next

RM Pre 26 D shall be constructed according to well established principles of good language design.

Quote from AntC on October 3, 2022, 6:36 am
Quote from Erwin on October 2, 2022, 12:56 pm

The effects you'd be getting if users start querying the catalog to find, e.g. all the relvars that have an attribute named 'RELVARNAME' (the query might be something like 'RELVARATTRIBUTE WHERE ATTRIBUTENAME = "RELVARNAME" {RELVARNAME}' are as follows :

...

Is the suffixed-in-curly-braces doing Tutorial D's projection? I think I mentioned I'd prefer Tutorial D had an explicit operator for projection. If it is projection, please show parens ( ... ) so I can see what the projection scopes over.

There's a convention observed in 'Good language design' since at least ALGOL 60: I'll call it context-sensitive syntax; context-insensitive semantics. SQL doesn't observe it; COBOL doesn't; all the C-family languages do. Assembly languages don't in general -- but then they use column positions/layout to distinguish opcode vs arguments. This is much more to do with helping a human reader of code than the compiler.

  • There's a small set of 'reserved words';
  • any other names/words/lexemes the reader can tell from their surrounding syntax whether that's a type name or variable name or function name;
  • (or relvar name or attribute name -- as I'd like for a D).

Let me change Erwin's example to use names that are unfamiliar/not meaningful to the casual reader:

  • SQUOGGLE WHERE SQUIGGLE = SQUONGLE {SQUANGLE}

This syntax is ambiguous -- in the same way that ALGOL's nested if ... then if ... then ... else ... is ambiguous: does the else branch from the nested if or the outermost if? (So when I said above "observed since ALGOL 60": ALGOL got this wrong; and they readily admit to it. Fixed in BCPL, ALGOL 68, C, ...)

  • WHERE is a reserved word (used infix), = is an infix operator fair enough.
  • SQUONGLE might be a Relation-valued attribute (or TVA); then
  • is {SQUANGLE} projecting on it? or
  • projecting on the outer SQUOGGLE WHERE ...?
  • I shouldn't have to know the attribute names of SQUOGGLE and their types to be able to mentally parse that expr.
  • And in general, WHERE-conditions might be long and gnarly; I want a visual clue I've got to the end; and am back at the relation-denoting part of the expression.

Now Erwin/SIRA_PRISE wants to compound this:

PROJECT ( RESTRICT ( RELVARATTRIBUTE , EQ ( ATTRIBUTENAME , NAME(RELVARNAME) ) ) , (RELVARNAME) )

I suspect for the casual reader to understand this, they need to know NAME( ) is not just some arbitrary function, but a reserved word with special syntactically-oriented significance. Probes:

  • Can I define a function foo(X) = NAME(X)?
  • Can the argument to NAME( ) be some arbitrary expression? What's the required type for that expression?
  • Then does RELVARNAME appearing as operand to NAME( ) denote the same thing as (RELVARNAME) appearing as the second argument to PROJECT( )?

Similar probes for PROJECT( ) and RESTRICT( ). The second arg to PROJECT( ..., (RELVARNAME) ) must be in parens, because it can be a comma-list. Are comma-lists available in general for expressions in the language? Can I assign a comma-list to a variable? Can I build a comma-list by (say) concatenating from comma-lists? (Also the second argument to RESTRICT( ) is a kinda proto-expression that is to be evaluated for each tuple in RESTRICT( )'s first argument. Can I assign a proto-expression to a variable? Can I build a proto-expression by nesting proto-expressions with operators?)

Tutorial D is at least clear that BLAH { ATTR1, ATTR2 } is hard-coded syntax. The stuff inside { } must be attribute names, not attribute-name-valued expressions -- there's no such thing. And that a WHERE-condition is also special syntax, in which any attribute names are to be bound successively to the attributes in each tuple of the relation. (Whereas variable names denote as usual the currently assigned value of that variable.)

But still I'm not happy: I have to look to the left of { }: is it TUP{ } or REL{ }? And are they appearing in expression-position or type-position? Then there's different hard-coded syntax inside the { }; and I have to be aware of those reserved words and their variant spellings.

  • SQUOGGLE WHERE SQUIGGLE = SQUONGLE {SQUANGLE}

This syntax is ambiguous -- in the same way that ALGOL's nested if ... then if ... then ... else ... is ambiguous: does the else branch from the nested if or the outermost if? (So when I said above "observed since ALGOL 60": ALGOL got this wrong; and they readily admit to it. Fixed in BCPL, ALGOL 68, C, ...)

While I agree with most of what you say here, this one is a bit unfair. Operators usually bind to adjacent terms in the absence of bracketing. If anything the problem here is the lack of an explicit operator: the {} delimits a heading aka list of attribute names and as used here also implies projection. Elsewhere the same construct does not.

  • WHERE is a reserved word (used infix), = is an infix operator fair enough.
  • SQUONGLE might be a Relation-valued attribute (or TVA); then
  • is {SQUANGLE} projecting on it? or
  • projecting on the outer SQUOGGLE WHERE ...?
  • I shouldn't have to know the attribute names of SQUOGGLE and their types to be able to mentally parse that expr.
  • And in general, WHERE-conditions might be long and gnarly; I want a visual clue I've got to the end; and am back at the relation-denoting part of the expression.

Yes. The RHS argument to the WHERE operator is a boolean valued expression, not a primary. You have to know how to parse it. My preference is:

  • SQUOGGLE.WHERE(SQUIGGLE = SQUONGLE.{SQUANGLE})

Now Erwin/SIRA_PRISE wants to compound this:

PROJECT ( RESTRICT ( RELVARATTRIBUTE , EQ ( ATTRIBUTENAME , NAME(RELVARNAME) ) ) , (RELVARNAME) )

But still I'm not happy: I have to look to the left of { }: is it TUP{ } or REL{ }? And are they appearing in expression-position or type-position? Then there's different hard-coded syntax inside the { }; and I have to be aware of those reserved words and their variant spellings.

Language design is hard. TD is loosely based on PL/I and SQL, and suffers from it. There are plenty of popular modern day languages that are just as bad.

Andl - A New Database Language - andl.org
Quote from AntC on October 4, 2022, 7:53 am

RM Pre 26 D shall be constructed according to well established principles of good language design.

Let me change Erwin's example to use names that are unfamiliar/not meaningful to the casual reader:

  • SQUOGGLE WHERE SQUIGGLE = SQUONGLE {SQUANGLE}
  • SQUONGLE might be a Relation-valued attribute (or TVA); then
  • is {SQUANGLE} projecting on it? or
  • projecting on the outer SQUOGGLE WHERE ...?
  • I shouldn't have to know the attribute names of SQUOGGLE and their types to be able to mentally parse that expr.
  • And in general, WHERE-conditions might be long and gnarly; I want a visual clue I've got to the end; and am back at the relation-denoting part of the expression.

Now Erwin/SIRA_PRISE wants to compound this:

PROJECT ( RESTRICT ( RELVARATTRIBUTE , EQ ( ATTRIBUTENAME , NAME(RELVARNAME) ) ) , (RELVARNAME) )

I suspect for the casual reader to understand this, they need to know NAME( ) is not just some arbitrary function, but a reserved word with special syntactically-oriented significance. Probes:

  • Can I define a function foo(X) = NAME(X)?
  • Can the argument to NAME( ) be some arbitrary expression? What's the required type for that expression?
  • Then does RELVARNAME appearing as operand to NAME( ) denote the same thing as (RELVARNAME) appearing as the second argument to PROJECT( )?

Similar probes for PROJECT( ) and RESTRICT( ). The second arg to PROJECT( ..., (RELVARNAME) ) must be in parens, because it can be a comma-list. Are comma-lists available in general for expressions in the language? Can I assign a comma-list to a variable? Can I build a comma-list by (say) concatenating from comma-lists? (Also the second argument to RESTRICT( ) is a kinda proto-expression that is to be evaluated for each tuple in RESTRICT( )'s first argument. Can I assign a proto-expression to a variable? Can I build a proto-expression by nesting proto-expressions with operators?)

Tutorial D is at least clear that BLAH { ATTR1, ATTR2 } is hard-coded syntax. The stuff inside { } must be attribute names, not attribute-name-valued expressions -- there's no such thing. And that a WHERE-condition is also special syntax, in which any attribute names are to be bound successively to the attributes in each tuple of the relation. (Whereas variable names denote as usual the currently assigned value of that variable.)

But still I'm not happy: I have to look to the left of { }: is it TUP{ } or REL{ }? And are they appearing in expression-position or type-position? Then there's different hard-coded syntax inside the { }; and I have to be aware of those reserved words and their variant spellings.

If you're specifically asking "can I do this in Erwin's language", then:

"Can I define a function foo(X) = NAME(X)?" : Would lead me too far to answer in detail, but (1) why would you and (2) if it's a problem somehow, then how is it not a problem in just any language that allows you to define your own functions ?

"Can the argument to NAME( ) be some arbitrary expression? What's the required type for that expression?" : the strict syntax for value selectors such as this is NAME(<componentname>(<expression>)) (with as many componentnames and expressions of matching type as are needed to provide the full set of component values for the possrep at hand).  If and only if there is only one possrep that has exactly one component (and I think it must in that case also be the only possrep altogether but I'm no longer sure), then the shorthand NAME(<expression>) is allowed and the expression type must match the component type of that possrep.

(In parentheses : the 'RELVARNAME' portion in 'NAME(RELVARNAME)' could be a valid reference to an attribute named 'RELVARNAME' (in my example, it even is).  But the type of that attribute is NAME, thus doesn't match the required component type, thus ultimately doesn't get interpreted as such, but as the STRING value it's intended to be.  IF it were the case that the type of that RELVARNAME attribute is itself STRING, then effectively the RESTRICT condition would amount to (+-, because disregarding the type mismatch error in the following) EQ ( ATTRIBUTENAME , RELVARNAME ) which wouldn't be the intent, presumably.  As you say, avoiding this is probably only possible by adding intermediate keywords that allow the user to suggest/declare the intended role of the word he's writing, but inevitably that would make the shorthand less short again.  And at any rate, sticking to the strict syntax does exactly that and is always going to work.  End of in parentheses.)

"Then does RELVARNAME appearing as operand to NAME( ) denote the same thing as (RELVARNAME) appearing as the second argument to PROJECT( )?"  See above.  With names coinciding (with other names, with values, ...) it could indeed denote the same thing even if not intended, or not denote the same thing even if indeed intended.  Turning to the explicitly verbose syntax should always resolve the problem.

"Are comma-lists available in general for expressions in the language? Can I assign a comma-list to a variable? Can I build a comma-list by (say) concatenating from comma-lists? ( ... . Can I assign a proto-expression to a variable? Can I build a proto-expression by nesting proto-expressions with operators?" : No.  Is that a necessary condition for "good language design" ?  If so, was pre-lambda java then a badly designed language ?  In fact, is any language that doesn't have lambda's "badly designed" on account of just that fact ?

 

And I have no idea what it is that I am "compounding" with what.

Quote from AntC on October 4, 2022, 7:53 am

Tutorial D is at least clear that BLAH { ATTR1, ATTR2 } is hard-coded syntax. The stuff inside { } must be attribute names, not attribute-name-valued expressions -- there's no such thing.

Hence "operator generators".

Quote from dandl on October 4, 2022, 9:30 am
  • SQUOGGLE WHERE SQUIGGLE = SQUONGLE {SQUANGLE}

This syntax is ambiguous -- in the same way that ALGOL's nested if ... then if ... then ... else ... is ambiguous: does the else branch from the nested if or the outermost if? (So when I said above "observed since ALGOL 60": ALGOL got this wrong; and they readily admit to it. Fixed in BCPL, ALGOL 68, C, ...)

While I agree with most of what you say here, this one is a bit unfair. Operators usually bind to adjacent terms in the absence of bracketing. If anything the problem here is the lack of an explicit operator: the {} delimits a heading aka list of attribute names and as used here also implies projection. Elsewhere the same construct does not.

Operators have a precedence system; then the chief purpose for bracketing is to override the usual precedence. I guess usually the projection (invisible) operator would be looser-binding than WHERE; and it would be unusual to have RVA or TVAs in a WHERE-condition, so your suggested bracketing would be the override case, and less common. Good. But without a visible operator, the casual reader must observe the {   }; mentally eliminate the uses of {   } that aren't projection; mentally insert that invisible operator; then apply the invisible precedence rules.

Language design is hard. TD is loosely based on PL/I and SQL, and suffers from it. There are plenty of popular modern day languages that are just as bad.

In the thread this one sprouted from, I'm introducing three functions/operators that are first-class: they call-by-value their arguments, the arguments can be some arbitrary expression of type TUPLE, they can be a TUPLE literal -- whose syntax is the same as any other TUPLE literal -- including as used inside a REL{ } literal, you can define a function that calls any of those three or some compounding of the three -- for example I defined TupleJoin in terms of them.

To enumerate the "suffers": SQL (and COBOL) suffer from a plethora of reserved words -- many used in only a single construct; with idiosyncratic syntax inside each construct; and idiosyncratic calling modes for each 'component' -- I won't call those 'arguments' (call by value, call by simple name, call by reference, anonymous lambda expr for the WHERE-condition, ...) By the time of Tutorial D there was no need to fall into those same traps.

Quote from Erwin on October 4, 2022, 3:01 pm
Quote from AntC on October 4, 2022, 7:53 am

RM Pre 26 D shall be constructed according to well established principles of good language design.

Let me change Erwin's example to use names that are unfamiliar/not meaningful to the casual reader:

  • SQUOGGLE WHERE SQUIGGLE = SQUONGLE {SQUANGLE}
  • SQUONGLE might be a Relation-valued attribute (or TVA); then
  • is {SQUANGLE} projecting on it? or
  • projecting on the outer SQUOGGLE WHERE ...?
  • I shouldn't have to know the attribute names of SQUOGGLE and their types to be able to mentally parse that expr.
  • And in general, WHERE-conditions might be long and gnarly; I want a visual clue I've got to the end; and am back at the relation-denoting part of the expression.

Now Erwin/SIRA_PRISE wants to compound this:

PROJECT ( RESTRICT ( RELVARATTRIBUTE , EQ ( ATTRIBUTENAME , NAME(RELVARNAME) ) ) , (RELVARNAME) )

I suspect for the casual reader to understand this, they need to know NAME( ) is not just some arbitrary function, but a reserved word with special syntactically-oriented significance. Probes:

  • Can I define a function foo(X) = NAME(X)?
  • Can the argument to NAME( ) be some arbitrary expression? What's the required type for that expression?
  • Then does RELVARNAME appearing as operand to NAME( ) denote the same thing as (RELVARNAME) appearing as the second argument to PROJECT( )?

Similar probes for PROJECT( ) and RESTRICT( ). The second arg to PROJECT( ..., (RELVARNAME) ) must be in parens, because it can be a comma-list. Are comma-lists available in general for expressions in the language? Can I assign a comma-list to a variable? Can I build a comma-list by (say) concatenating from comma-lists? (Also the second argument to RESTRICT( ) is a kinda proto-expression that is to be evaluated for each tuple in RESTRICT( )'s first argument. Can I assign a proto-expression to a variable? Can I build a proto-expression by nesting proto-expressions with operators?)

Tutorial D is at least clear that BLAH { ATTR1, ATTR2 } is hard-coded syntax. The stuff inside { } must be attribute names, not attribute-name-valued expressions -- there's no such thing. And that a WHERE-condition is also special syntax, in which any attribute names are to be bound successively to the attributes in each tuple of the relation. (Whereas variable names denote as usual the currently assigned value of that variable.)

But still I'm not happy: I have to look to the left of { }: is it TUP{ } or REL{ }? And are they appearing in expression-position or type-position? Then there's different hard-coded syntax inside the { }; and I have to be aware of those reserved words and their variant spellings.

If you're specifically asking "can I do this in Erwin's language", then:

"Can I define a function foo(X) = NAME(X)?" : Would lead me too far to answer in detail, but (1) why would you ...

Because (in general) I want a version of a Selector that supplies initial values for some of the PossRep components. I want a specialised PROJECT/SELECT that gets the Supplier Name and Quantity for parameter Product.

and (2) if it's a problem somehow, then how is it not a problem in just any language that allows you to define your own functions ?

I think (I'm guessing) because your different pseudo-functions have different calling modes for different arguments. I'm reminded of ALGOL60 call-by-value vs call-by-simple-name vs call-by-reference vs call-by-fancy-wotsit-per-Jensen's-device. In an ALGOL function decl you not only give the arguments' types but also their calling modes. I'm not saying SIRA_PRISE has those same modes; I am saying you need to know more about what appears to be a function than merely the types of its arguments. For example the second arg to PROJECT( ) must be a literal attribute-name-list -- there's no mechanism to pass that in via a variable or via an expression that catenates two name-lists. Neither (I suspect) is there a way to user-declare a function to have an argument that must be a literal attribute-name-list.

"Can the argument to NAME( ) be some arbitrary expression? What's the required type for that expression?" : the strict syntax for value selectors such as this is NAME(<componentname>(<expression>)) (with as many componentnames and expressions of matching type as are needed to provide the full set of component values for the possrep at hand).  If and only if there is only one possrep that has exactly one component (and I think it must in that case also be the only possrep altogether but I'm no longer sure), then the shorthand NAME(<expression>) is allowed and the expression type must match the component type of that possrep.

So <componentname> must be a literal, not a variable or component-valued-expression?

(In parentheses : the 'RELVARNAME' portion in 'NAME(RELVARNAME)' could be a valid reference to an attribute named 'RELVARNAME' (in my example, it even is).  But the type of that attribute is NAME, thus doesn't match the required component type, thus ultimately doesn't get interpreted as such, but as the STRING value it's intended to be.

Ok this is the nub of what I don't like. The argument to NAME( ) must be of type STRING; but your example used what appeared to be an attribute name, that in other positions 'stood for' the value of that attribute being projected from a relation value. So there seems to be implicit type conversion going on. So apparently identical bits of syntax meaning different things in different contexts -- depending on which function they were arguments to.

  IF it were the case that the type of that RELVARNAME attribute is itself STRING, then effectively the RESTRICT condition would amount to (+-, because disregarding the type mismatch error in the following) EQ ( ATTRIBUTENAME , RELVARNAME ) which wouldn't be the intent, presumably.  As you say, avoiding this is probably only possible by adding intermediate keywords that allow the user to suggest/declare the intended role of the word he's writing, but inevitably that would make the shorthand less short again.  And at any rate, sticking to the strict syntax does exactly that and is always going to work.  End of in parentheses.)

Requiring quote marks round arguments that must be strings, and which don't stand for the value of some same-named variable (or attribute) would be more-than-justified longhand. Note that Haskell (for example) makes a syntactic difference between variables (start lower case true) vs Selector (that is, Constructor like True) must start upper case vs String "True". That is hugely valuable when reading somebody else's code, where I don't know what user-defined types and components they have.

"Then does RELVARNAME appearing as operand to NAME( ) denote the same thing as (RELVARNAME) appearing as the second argument to PROJECT( )?"  See above.  With names coinciding (with other names, with values, ...) it could indeed denote the same thing even if not intended, or not denote the same thing even if indeed intended.  Turning to the explicitly verbose syntax should always resolve the problem.

"Are comma-lists available in general for expressions in the language? Can I assign a comma-list to a variable? Can I build a comma-list by (say) concatenating from comma-lists? ( ... . Can I assign a proto-expression to a variable? Can I build a proto-expression by nesting proto-expressions with operators?" : No.  Is that a necessary condition for "good language design" ?  If so, was pre-lambda java then a badly designed language ?  In fact, is any language that doesn't have lambda's "badly designed" on account of just that fact ?

 

I think lambda expressions are a red herring here. You could always declare a function that would take the same arguments and return the same result as that lambda; and then pass that function itself (not the result of an invocation of that function); then the callee could invoke that function. (For example apply it to each tuple that met some test.) That was supported back to ALGOL 60. lambda-expressions just make that a whole lot more lightweight for an ad-hoc used-once-only function-to-pass.

So no, lacking lambda-exprs does not make a language "badly designed".

And I have no idea what it is that I am "compounding" with what.

SIRA_PRISE on the face of it has less special-purpose syntax than Tutorial D. But as it turns out nearly none of (what looks like) functions are functions; each has ad-hoc syntax for each argument; each has special restriction as to what can appear in those positions; and those restrictions go way beyond merely the required type of each argument.

In this case, dedicated syntax would be _better_ instead of each function -- because I wouldn't be bamboozled into thinking RELVARNAME is the same thing in NAME(RELVARNAME) vs PROJECT( ..., (RELVARNAME) ). I couldn't for example assign RELVARNAME to a variable, then use that variable in place of each appearance.

So it's the confusion and ad-hockery that SIRA_PRISE is compounding.

Hence "operator generators"

Hmm. TTM doesn't use that term, so this is an implementation detail. TTM does use "TUPLE type generator" and "RELATION type generator". The Tutorial D spec says [Footnote 2 re SQL] "... CHAR and VARCHAR... as type generators". Later there's "ARRAY type generator". I think all this shows is the author(s) don't understand polymorphism and parametric types -- as don't SQL or COBOL. I don't think our language design needs to be restricted to the 1980's.

My three suggested tuple operators are polymorphic enough to cope with a TUPLE type that includes attributes: PNO at type PartNo; QTY at some numeric type; and possibly other attributes unknown. I don't expect to 'generate' a different under-the-hood operator for each possible full tuple type meeting that polymorphic type. (That's the sort of implementation detail I was hoping would arise from discussion in that thread.)

Quote from AntC on October 5, 2022, 5:33 am

"Can I define a function foo(X) = NAME(X)?" : Would lead me too far to answer in detail, but (1) why would you ...

Because (in general) I want a version of a Selector that supplies initial values for some of the PossRep components. I want a specialised PROJECT/SELECT that gets the Supplier Name and Quantity for parameter Product.

Then you are doing the very same thing with your X as I did with RELVARNAME : your X passed to foo() is not the same thing as the X thing passed to NAME() : in particular, your first X is a proper subset (strictly less component values) of your second X.  Pot...kettle...

"I am saying you need to know more about what appears to be a function than merely the types of its arguments. For example the second arg to PROJECT( ) must be a literal attribute-name-list -- there's no mechanism to pass that in via a variable or via an expression that catenates two name-lists. Neither (I suspect) is there a way to user-declare a function to have an argument that must be a literal attribute-name-list."

Yes, and all of that is feature, not bug.  It _is_ conceivable to devise a language that facilitates passing in projection specs, rename specs, where specs, extend specs, what have you, as values known only at run time.  You know what that destroys and in Tutorial D, the authors didn't want that.  I didn't want that in SIRA_PRISE either.  I no longer remember by heart whether that also holds for TTM per se.

As for "you need to know more about what appears to be a function than merely the types of its arguments" : I'd say that's pretty normal and wouldn't expect it otherwise.  You're never going to make sense of invocations of PROJECT(), RENAME(), EXTEND(), if you don't know _what_ those things are.  You're never going to make sense of an operator invocation if you don't know what that operator does (its semantics), so "merely the types of its arguments" is just never going to suffice for "understanding someone else's code".  And the same holds for selector invocations : you're never going to make sense of someone else's code if you don't understand(*) the type system that code is supposed to run in.  You appear to say it's a bug, I say it's a feature.

(*) I will readily acknowledge that what you actually wrote is "READING someone else's code" and not "UNDERSTANDING someone else code".  In my defense, I put up that there is no purpose to "reading" if there is no intent on your part for the "reading" to lead to "understanding".

Quote from Erwin on October 5, 2022, 7:59 am

"I am saying you need to know more about what appears to be a function than merely the types of its arguments. For example the second arg to PROJECT( ) must be a literal attribute-name-list -- there's no mechanism to pass that in via a variable or via an expression that catenates two name-lists. Neither (I suspect) is there a way to user-declare a function to have an argument that must be a literal attribute-name-list."

Yes, and all of that is feature, not bug.  It _is_ conceivable to devise a language that facilitates passing in projection specs, rename specs, where specs, extend specs, what have you, as values known only at run time.  You know what that destroys and in Tutorial D, the authors didn't want that.  I didn't want that in SIRA_PRISE either.  I no longer remember by heart whether that also holds for TTM per se.

I guess the problem in the framing of TTM is it's prescribing/proscribing a stand-alone language. If you start from nothing "good language design" is liable to mean "like the language I'm most familiar with". (And most of us -- even for our favourite language -- have small gripes, so that means "like an idealised version of ...".)

Whereas I've never wanted to start from nothing. From TTM I've tried to boil-down to properties of D-ness to be absorbed into an existing language -- preferably in a way that's uniform with its current design. (On the basis that the wisdom of the masses is more likely to evolve 'good language design' than starting again.)

Still that list in your second paragraph seems unnecessarily complicated.

  • "Projection specs"? Use a tuple to give the attribute names. Don't need to invent anything more.
  • "Rename specs"? Combine REMOVE/EXTEND -- as does Appendix A; we already have a means to extract the value at some attribute.
  • "Extend specs"? Just glue two tuples together. "Whatever" there needs a way to specify they can't have any attribute names in common. That's a property of their tuple-types. IOW that projecting one on the other has-same-type-as projecting the other on the one has-same-type-as TUPLE{ }. (Which shows why the best thing to use as a "Projection spec" is a tuple/its type.)
  • "as values known only at run time"? No you're not paying attention: their types must be known at compile time.
  • "You know what that destroys"? Yes: it prevents ad-hockery; it destroys the need for a plethora of special-purpose operators, each with idiosyncratic rules about their arguments' syntax and semantics. I suspect that's not what you mean -- so no, I don't follow what you think is wrong.

As for "you need to know more about what appears to be a function than merely the types of its arguments" : I'd say that's pretty normal and wouldn't expect it otherwise.  You're never going to make sense of invocations of PROJECT(), RENAME(), EXTEND(), if you don't know _what_ those things are.  You're never going to make sense of an operator invocation if you don't know what that operator does (its semantics), so "merely the types of its arguments" is just never going to suffice for "understanding someone else's code".

Theorems for free!; Propositions as Types.

And the same holds for selector invocations : you're never going to make sense of someone else's code if you don't understand(*) the type system that code is supposed to run in.  You appear to say it's a bug, I say it's a feature.

(*) I will readily acknowledge that what you actually wrote is "READING someone else's code" and not "UNDERSTANDING someone else code".  In my defense, I put up that there is no purpose to "reading" if there is no intent on your part for the "reading" to lead to "understanding".

I think what SIRA_PRISE demonstrates is there's no intent on the part of the WRITER of the code that it should lead to understanding. And even the original author becomes a reader when they come back to maintain it.

Page 1 of 2Next