The Forum for Discussion about The Third Manifesto and Related Matters

Please or Register to create posts and topics.

Update to Tutorial D, July 2020

Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
  WRITELN PI * E;
  WRITELN E + PI;
  WRITELN S_P UNION S_P;
END;

That might not be the exact syntax, because -- as I recall -- it was removed from Tutorial D several revisions ago. I subsequently removed it from Rel and Hugh has clearly re-deprecated it in Tutorial D at the start of this thread.

Note that PI, E, and S_P in the above are constants; they can't be re-assigned.

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
WRITELN PI * E;
WRITELN E + PI;
WRITELN S_P UNION S_P;
END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN; WRITELN PI * E; WRITELN E + PI; WRITELN S_P UNION S_P; END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
  WRITELN PI * E;
  WRITELN E + PI;
  WRITELN S_P UNION S_P;
END;

That might not be the exact syntax, because -- as I recall -- it was removed from Tutorial D several revisions ago. I subsequently removed it from Rel and Hugh has clearly re-deprecated it in Tutorial D at the start of this thread.

Note that PI, E, and S_P in the above are constants; they can't be re-assigned.

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

Looking back through my lengthy correspondence with Chris on this subject, I find it hard to pin down exactly where WITH on statements is flawed.  I know that we were worried about its use on multiple assignments where two or more assigns have the same target.  I asked Chris if he could give a good answer to Dave's question but all he could come up with was:

"WITH (...) : WITH (...) : ... WITH (...) : nonwith stmt body ;

"I'm not sure how to define the semantics of such a construct.  (Note that the WITH clauses aren't nested but are all at the same level.) "

I can add that in my case I was encouraged by Rel's current failure to support WITH on statements, guessing that Dave must have run nto implementation problems.

Perhaps for now removal of WITH on statements is just a safety play (under The Principle of Cautious Design).

Hugh

Coauthor of The Third Manifesto and related books.
Quote from Hugh on July 15, 2020, 2:26 pm
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
WRITELN PI * E;
WRITELN E + PI;
WRITELN S_P UNION S_P;
END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN; WRITELN PI * E; WRITELN E + PI; WRITELN S_P UNION S_P; END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
  WRITELN PI * E;
  WRITELN E + PI;
  WRITELN S_P UNION S_P;
END;

That might not be the exact syntax, because -- as I recall -- it was removed from Tutorial D several revisions ago. I subsequently removed it from Rel and Hugh has clearly re-deprecated it in Tutorial D at the start of this thread.

Note that PI, E, and S_P in the above are constants; they can't be re-assigned.

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

Looking back through my lengthy correspondence with Chris on this subject, I find it hard to pin down exactly where WITH on statements is flawed.  I know that we were worried about its use on multiple assignments where two or more assigns have the same target.  I asked Chris if he could give a good answer to Dave's question but all he could come up with was:

"WITH (...) : WITH (...) : ... WITH (...) : nonwith stmt body ;

"I'm not sure how to define the semantics of such a construct.  (Note that the WITH clauses aren't nested but are all at the same level.) "

I can add that in my case I was encouraged by Rel's current failure to support WITH on statements, guessing that Dave must have run nto implementation problems.

No, it was removed only because the Tutorial D specification removed it. I'll have to check where/when, as I don't recall.

When it was in Rel, it worked fine. There were no implementation problems that I recall. I simply treated a statement like WITH (A := x) : WITH (B := A + 2) : WITH (C := z) : nonwith stmt body ; as being equivalent to WITH (A := x, B := A + 2, C := z) : nonwith stmt body ; which (if I recall correctly) was precisely equivalent to a hypothetical...

CONST A := x;
CONST B := A + 2;
CONST C := z;
nonwith stmt body;

...for which it should be straightforward to either retain those semantics -- the constant definitions occur sequentially -- or treat it (the same) as multiple assignment.

Either would be fine.

Perhaps for now removal of WITH on statements is just a safety play (under The Principle of Cautious Design).

Does it resolve the problem, or does it simply inject it into INSERT, UPDATE, and EXTEND?

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Hugh on July 15, 2020, 2:26 pm
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
WRITELN PI * E;
WRITELN E + PI;
WRITELN S_P UNION S_P;
END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN; WRITELN PI * E; WRITELN E + PI; WRITELN S_P UNION S_P; END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
  WRITELN PI * E;
  WRITELN E + PI;
  WRITELN S_P UNION S_P;
END;

That might not be the exact syntax, because -- as I recall -- it was removed from Tutorial D several revisions ago. I subsequently removed it from Rel and Hugh has clearly re-deprecated it in Tutorial D at the start of this thread.

Note that PI, E, and S_P in the above are constants; they can't be re-assigned.

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

Looking back through my lengthy correspondence with Chris on this subject, I find it hard to pin down exactly where WITH on statements is flawed.  I know that we were worried about its use on multiple assignments where two or more assigns have the same target.  I asked Chris if he could give a good answer to Dave's question but all he could come up with was:

"WITH (...) : WITH (...) : ... WITH (...) : nonwith stmt body ;

"I'm not sure how to define the semantics of such a construct.  (Note that the WITH clauses aren't nested but are all at the same level.) "

 

And in thinking more (though, admittedly, perhaps superficially) about it, is there really any semantically-significant difference -- in terms of WITH -- between this:

OPERATOR fn(x INT, y RATIONAL, z CHAR) RETURNS BOOLEAN;
   ...
   RETURN ...;
END OPERATOR;

p := WITH (x := ..., y := ..., z := ...) : fn(x, y, z);

And this?

WITH (x := ..., y := ..., z := ...) : BEGIN
   ...
   p := ....;
END;

 

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

Thank you, that makes perfect sense.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

So the current WITH establishes a new scope in which to evaluate an expression, allowing the expression to be decomposed into a series of steps, mainly (I assume) for improved readability, but also allowing one to avoid evaluating the same sub-expression multiple times.

Each WITH can define multiple constant values separated by commas. And each inner expression could itself contain further WITH constructs, allowing arbitrarily nested local scopes. And potentially the reuse of names in inner scopes conflicting with those in outer scopes ('shadowing' as discussed previously). All left to the implementer to decide.

Extending WITH to a general statement construct seems consistent with the general philosophy of block-structured languages. It seems to do no harm, and it allows expression decomposition and de-duplication to be extended across more than one assignment and/or update operator. Seems a good thing to me.

Extending WITH to updates (which means UPDATE, INSERT and DELETE, for completeness) has the same effect: allowing decomposition and de-duplication across the parts of an update (aka assignment). Possibly the same ideas extend to SUMMARIZE, EXTEND and other shorthands, but these are just expressions anyway, so weren't they already covered?

There seem to be 3 cases to consider:

  1. Allow WITH for any expression, as part of the computation of a single value
  2. Allow WITH for any single statement, as part of a single update/assignment
  3. Allow WITH for any statement or block, as a general capability of the language.

I would find it hard to stop short of No 3. You have other extensions in your implementation, why not this one? As long as you provide full syntactic compliance with what is defined, extras are yours to choose.

Andl - A New Database Language - andl.org

Looking back through my lengthy correspondence with Chris on this subject, I find it hard to pin down exactly where WITH on statements is flawed.  I know that we were worried about its use on multiple assignments where two or more assigns have the same target.  I asked Chris if he could give a good answer to Dave's question but all he could come up with was:

"WITH (...) : WITH (...) : ... WITH (...) : nonwith stmt body ;

"I'm not sure how to define the semantics of such a construct.  (Note that the WITH clauses aren't nested but are all at the same level.) "

I can add that in my case I was encouraged by Rel's current failure to support WITH on statements, guessing that Dave must have run nto implementation problems.

No, it was removed only because the Tutorial D specification removed it. I'll have to check where/when, as I don't recall.

When it was in Rel, it worked fine. There were no implementation problems that I recall. I simply treated a statement like WITH (A := x) : WITH (B := A + 2) : WITH (C := z) : nonwith stmt body ; as being equivalent to WITH (A := x, B := A + 2, C := z) : nonwith stmt body ; which (if I recall correctly) was precisely equivalent to a hypothetical...

CONST A := x;
CONST B := A + 2;
CONST C := z;
nonwith stmt body;

...for which it should be straightforward to either retain those semantics -- the constant definitions occur sequentially -- or treat it (the same) as multiple assignment.

There is a difference.

  • WITH(a,b,c) looks like a single scope
  • WITH(a):WITH(b):WITH(c): looks like nested scopes but might not be
  • WITH(a): x+WITH(b): y has to be nested, surely?
Andl - A New Database Language - andl.org
Quote from Hugh on July 10, 2020, 2:40 pm
Quote from Dave Voorhis on July 9, 2020, 3:08 pm
Quote from Hugh on July 9, 2020, 11:22 am

Here's a response from Chris Date:

Here's a trivial example of the kind of thing I had in mind: 

EXTEND S :

{ WITH ( temp := ( SP MATCHING S ) WHERE QTY > 500 AND NOT ( PNO = 'P1' )  ) :

A := SUM ( temp , QTY ) ,

B := MAX ( temp , QTY ) ,

C := MIN ( temp , QTY ) ,

D := COUNT ( temp ) }

Obviously the definition of temp could be arbitrarily complex in practice.

Obviously, temp is in scope for each of the assigns, overriding any existing definition under normal block-scoping rules.

What if, in Chris's example, the name chosen for temp is QTY?

I kind of see the point, I guess, but how is it superior to this?

EXTEND S : {
temp := ( SP MATCHING S ) WHERE THE_QTY(QTY) > 200 AND NOT ( P# = P#('P1') ),
A := SUM ( temp , THE_QTY(QTY) ) ,
B := MAX ( temp , THE_QTY(QTY) ) ,
C := MIN ( temp , THE_QTY(QTY) ) ,
D := COUNT ( temp )
} {ALL BUT temp}
EXTEND S : { temp := ( SP MATCHING S ) WHERE THE_QTY(QTY) > 200 AND NOT ( P# = P#('P1') ), A := SUM ( temp , THE_QTY(QTY) ) , B := MAX ( temp , THE_QTY(QTY) ) , C := MIN ( temp , THE_QTY(QTY) ) , D := COUNT ( temp ) } {ALL BUT temp}
EXTEND S : {
  temp := ( SP MATCHING S ) WHERE THE_QTY(QTY) > 200 AND NOT ( P# = P#('P1') ),
  A := SUM ( temp , THE_QTY(QTY) ) ,
  B := MAX ( temp , THE_QTY(QTY) ) ,
  C := MIN ( temp , THE_QTY(QTY) ) ,
  D := COUNT ( temp )
} {ALL BUT temp}

I'm all for syntactic additions that provide benefits, but to be honest I'm having a hard time seeing the benefits of WITH inside EXTEND, at least.

I feel like I'm missing something (that should be?) obvious.

(Note that I made a few small tweaks to the original to fit the relvar & type definitions supplied with Rel, as I don't like to post code I haven't run. :-)

I admit that your example is equivalent and only very slightly "inferior".  However, the idea doesn't work for the various UPDATE statements.

Btw, on reflection I find Chris's example rather contrived.  Why extend all tuples of S with exactly the same values?  One might as well extend TABLE_DEE with that lot.

Hugh

> Why extend all tuples of S with exactly the same values?

I think I can kinda maybe guess a difficulty here. Not sure it's what Chris is worried about (certainly his example doesn't illustrate it); not sure it's worth making a song and dance about. Here's a very contrived (and ugly) example. I'll readily agree this code could look prettier, but how? And perhaps there's some way to legislate for the prettier look(??) First a straw man (I know this won't work; perhaps that's why it was removed from Tutorial D)

 

WITH ( QTY2 := IF S# < 'S100' THEN QTY * 1.10 ELIF S# >= 'S100' AND S# < 'S500' THEN QTY * 1.15 ELSE QTY * 1.2 ) :
UPDATE SP : { QTY := QTY2 };

 

The intent is to uplift QTY by some long-and-complex-formula depending on other attributes in each tuple; a formula that you don't want to bury inside the UPDATE statement. IOW not update with a constant, as Hugh objects to Chris's example. Also QTY2 is a scalar, unlike temp being a relation value in Chris's example.

As I've written it that won't work, because QTY, S# are not in scope until you get inside the UPDATE statement -- that is, not until you know which relvar you're targeting, so bringing its attribute names into scope.

Now does Dave's counter example with the temp inside the UPDATE's {  } achieve that scoping? I'm guessing not, because Chris (and Dave follows) repeats S on rhs of the assignment, even though it's also the target of the outer EXTEND. Then (SP JOIN S) is also a constant and there's no point putting that expression inside the EXTEND. Note also the QTY,PNO are from that inner (SP JOIN S)not the outer S target of the EXTEND -- i.e. different-scoped QTY to any attribute names from S appearing on rhs of other assignments.

I also find Dave's counter 'excessive circumlocution': why EXTEND with an attribute temp that you then project away? Especially since temp is an RVA.

So the root of the difficulty here is those durned 'open expressions': the rhs of the assignment within scope of a relational update statement is to be an open-expression context, using attribute names from the update target, and the constant 'evaluated' (scare quotes) for each tuple that's getting updated/extended.

Then I suggest (guess) what Chris is reaching for is:

UPDATE SP :
  { WITH ( QTY2 := IF S# < 'S100' THEN QTY * 1.10 ELIF S# >= 'S100' AND S# < 'S500' THEN QTY * 1.15 ELSE QTY * 1.2 ) :
  QTY := QTY2 };

Note again QTY2 is a scalar constant, not a relcon. And the intent is the rhs of its assignment is an 'open expression' context in which the attribute names are from the outer SP. This example would have more force if QTY2 were used in several attribute-assignments of the UPDATE -- which is (more guessing) what Chris's example was trying to exercise.

I'll add my usual caveats that IANATDP (I am not a Tutorial D Programmer) and I've probably got my syntax wrong. Also I've not bothered with Dave's THE_QTY(QTY) business -- another bit of excessive circumlocution IMO.

Quote from Dave Voorhis on July 15, 2020, 5:02 pm
Quote from Hugh on July 15, 2020, 2:26 pm
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
WRITELN PI * E;
WRITELN E + PI;
WRITELN S_P UNION S_P;
END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN; WRITELN PI * E; WRITELN E + PI; WRITELN S_P UNION S_P; END;
WITH (PI := 3.14159, E := 2.71828, S_P := S JOIN P) BEGIN;
  WRITELN PI * E;
  WRITELN E + PI;
  WRITELN S_P UNION S_P;
END;

That might not be the exact syntax, because -- as I recall -- it was removed from Tutorial D several revisions ago. I subsequently removed it from Rel and Hugh has clearly re-deprecated it in Tutorial D at the start of this thread.

Note that PI, E, and S_P in the above are constants; they can't be re-assigned.

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

Looking back through my lengthy correspondence with Chris on this subject, I find it hard to pin down exactly where WITH on statements is flawed.  I know that we were worried about its use on multiple assignments where two or more assigns have the same target.  I asked Chris if he could give a good answer to Dave's question but all he could come up with was:

"WITH (...) : WITH (...) : ... WITH (...) : nonwith stmt body ;

"I'm not sure how to define the semantics of such a construct.  (Note that the WITH clauses aren't nested but are all at the same level.) "

I can add that in my case I was encouraged by Rel's current failure to support WITH on statements, guessing that Dave must have run nto implementation problems.

No, it was removed only because the Tutorial D specification removed it. I'll have to check where/when, as I don't recall.

When it was in Rel, it worked fine. There were no implementation problems that I recall. I simply treated a statement like WITH (A := x) : WITH (B := A + 2) : WITH (C := z) : nonwith stmt body ; as being equivalent to WITH (A := x, B := A + 2, C := z) : nonwith stmt body ; which (if I recall correctly) was precisely equivalent to a hypothetical...

CONST A := x;
CONST B := A + 2;
CONST C := z;
nonwith stmt body;

...for which it should be straightforward to either retain those semantics -- the constant definitions occur sequentially -- or treat it (the same) as multiple assignment.

Careful, those two forms could be different wrt scoping; then 'flattening' them as you show could give different semantics. OTOH any counter-example looks highly contrived and probably not what anyone would intend ...

WITH (A := x) :             //    suppose scalar var QTY in outer scope
  WITH (x := QTY) :         // different 'x', shadowing the outer 'x' assigned to 'A'
    V := SP WHERE QTY = x;  // contrast ... WHERE QTY = QTY would be pointless

 

Again I see a potential difficulty with scoping in 'open expression' contexts, in which mention of a relvar or relation expression brings into inner scope attribute names that are not visible in outer scopes.

I'm increasingly forming a view there's something fishy (in the sense of poor language design) with 'open expressions' in Tutorial D. I think attribute names should live in a different namespace than usual program variables and names; and that the syntax should provide different forms/contexts for where a name is to be taken as an attribute vs a variable/function/etc. The fishiness is because attribute names come into scope only as a result of mentioning a relvar/relational expression; there's no explicit declaration that introduces them.

Quote from dandl on July 16, 2020, 12:52 am
Quote from Dave Voorhis on July 10, 2020, 6:19 pm
Quote from dandl on July 10, 2020, 3:20 pm

What purpose does WITH serve, exactly? It looks like it just declares and initialises a variable local to a block, so how is it different from VAR?

It declares a set of constants in a well-defined and limited scope. I think it's the only way to declare constants in Tutorial D.

Thank you, that makes perfect sense.

In addition to the "expression" form, there used to be a "statement" form of WITH that allowed things like this:

I don't recall why the statement form of WITH was considered "ill-advised" and deprecated/removed. Hugh?

I have to say I balk at introducing WITH into UPDATE/INSERT/EXTEND but not as the general statement construct that it used to be (which I liked!)

But if there's a good justification for it not being a general statement construct and only putting it in UPDATE and INSERT (and EXTEND, I guess) then I'm happy to implement it in Rel.

So the current WITH establishes a new scope in which to evaluate an expression, allowing the expression to be decomposed into a series of steps, mainly (I assume) for improved readability, but also allowing one to avoid evaluating the same sub-expression multiple times.

Each WITH can define multiple constant values separated by commas. And each inner expression could itself contain further WITH constructs, allowing arbitrarily nested local scopes. And potentially the reuse of names in inner scopes conflicting with those in outer scopes ('shadowing' as discussed previously). All left to the implementer to decide.

Extending WITH to a general statement construct seems consistent with the general philosophy of block-structured languages. It seems to do no harm, and it allows expression decomposition and de-duplication to be extended across more than one assignment and/or update operator. Seems a good thing to me.

For comparison, Haskell as an inline/local-scope form of declaration:

<expr> ::= let { <decls> } in <expr>
         | ...

<decls> ::= <decl> [; <decls>]   -- could declare a simple name, or a function


let {x = 7; f(z) = z + 2} in let {y = 5} in f(x + y)

As per the discussion here, directly nesting a let inside a let as illustrated is usually pointless, but <expr> could be an arbitrarily complex expression, with a let embedded deep inside.

Extending WITH to updates (which means UPDATE, INSERT and DELETE, for completeness) has the same effect: allowing decomposition and de-duplication across the parts of an update (aka assignment). Possibly the same ideas extend to SUMMARIZE, EXTEND and other shorthands, but these are just expressions anyway, so weren't they already covered?

No I suspect not: those update forms introduce a relvar target (and SUMMARISE, EXTEND introduce a relexpr); its attribute names only come into scope as a consequence of the relvar name/expr appearing -- indeed there might be scalar vars in scope with the same name as attributes; those var names get shadowed even though there's no explicit declaration of the attribute names.

So we might want decomposition/de-duplication/etc within the body of the update forms, specifically for the attribute names in scope only within the body of the statement.

Arguably, because that's an 'open expression' context, the local decls should use a different keyword than WITH, because there's rather different semantics: the name is introduced per-tuple.

Quote from Hugh on July 9, 2020, 11:22 am

Here's a response from Chris Date:

Here's a trivial example of the kind of thing I had in mind: 

EXTEND S :

{ WITH ( temp := ( SP MATCHING S ) WHERE QTY > 500 AND NOT ( PNO = 'P1' )  ) :

A := SUM ( temp , QTY ) ,

B := MAX ( temp , QTY ) ,

C := MIN ( temp , QTY ) ,

D := COUNT ( temp ) }

Obviously the definition of temp could be arbitrarily complex in practice.

Obviously, temp is in scope for each of the assigns, overriding any existing definition under normal block-scoping rules.

 

Ah, here's a maybe better example/reworking of Chris's:

EXTEND S :

{ WITH ( temp := ( SP MATCHING REL{ TUP{ S# S# } } ) WHERE QTY > 500 AND NOT ( PNO = 'P1' )  ) :

A := SUM ( temp , QTY ) ,

B := MAX ( temp , QTY ) ,

C := MIN ( temp , QTY ) ,

D := COUNT ( temp ) }

IOW there's a different temp value for each tuple being extended in S; not a constant. It's extending S with an aggregate calculated from all and only the matching SP tuples. This looks a lot like the 'Image Relations' stuff.

In the relation 'literal' REL{ TUP{ S# S# } }, the second S# must be the 'current' S# (attribute name) coming from the outer-scoped S.

'Literal' in scare quotes because that attribute name S# is not a literal.

 

I note in passing that in an expression like SUM ( temp , QTY ), semantically the QTY is nested inside temp's scope because it's an attribute name whose validity depends on the relvar/expression; QTY is not the appearance of a var; also what must appear is an attribute name literal, not a var or expression evaluating to an attribute name.