The Forum for Discussion about The Third Manifesto and Related Matters

Please or Register to create posts and topics.

Which Result?

PreviousPage 4 of 8Next
Quote from Dave Voorhis on November 17, 2021, 9:30 am.

My point is that your considerations here -- the fundamental rules you're establishing -- are inescapably and unavoidably typeful.  You are defining your type system.

OK, that is fine. Let me check something.

Given (placeholders for) values p and q, types X, Y, Z and operator Op and a:b to mean "value a at type b"

Then in "my system", I would never allow

p:X Op q:Y

to ever return a different result than

p:Z Op q:Y

In other words, I see no need to ever know or care what type a value is at. No operator will ever give a different result for the same input values just because of the type that they are "currently carrying with them". Hence I have no need for the "value at type" concept (which, BTW is one reason why I don't think that I really have a type system, or at least, no more of a type system than is logically necessary - which, as far as I can see, is not very much of one).


or do you (or others) have a counter example of a given operator that has to (or at least, where it would be very highly desirable that it does) return a different result solely due to the type(s) that its input values are at?

For the avoidance of doubt. I'm not interested in examples from current programming languages - unless you think that such an example is reflective of where it has to be the case (rather than just happenstance of that particular language design)

 

Quote from Dave Voorhis on November 17, 2021, 9:30 am

Etc.  Indeed, you can abstract it a bit further:

<integer> <operator> <integer> evaluates to <integer>

Abstracting too far is not helpful. <integer> <operator> <integer> evaluates to at least <rational union boolean>   due to the = and \ operators, add in UNION and you get a <set> result.  Consider toString and such "type conversion" operators (which might not be dyadic, but nonetheless) and you soon approach <alpha>as the result type.

So abstract up to say <math operator> and you might be OK and get useful statements.. E.g.

<integer> <int math operator> <integer> evaluates to <integer>

and

<integer> <non-exotic math operator> <integer> evaluates to <Imaginary> or empty

and I would very much like to think that such statements can be arrived at by suitable database expressions over the "catalog" of  named sets we nominate as types and operator relations. Indeed, I would prefer them to be so "generated" rather than be proscribed by the "type designer"...

Quote from Paul Vernon on November 17, 2021, 10:39 am
Quote from Dave Voorhis on November 17, 2021, 9:30 am

Etc.  Indeed, you can abstract it a bit further:

<integer> <operator> <integer> evaluates to <integer>

Abstracting too far is not helpful. <integer> <operator> <integer> evaluates to at least <rational union boolean>   due to the = and \ operators, add in UNION and you get a <set> result.  Consider toString and such "type conversion" operators (which might not be dyadic, but nonetheless) and you soon approach <alpha>as the result type.

So abstract up to say <math operator> and you might be OK and get useful statements.. E.g.

<integer> <int math operator> <integer> evaluates to <integer>

and

<integer> <non-exotic math operator> <integer> evaluates to <Imaginary> or empty

The specifics are up to you. My point is that your considerations here are all about your type system.

and I would very much like to think that such statements can be arrived at by suitable database expressions over the "catalog" of  named sets we nominate as types and operator relations. Indeed, I would prefer them to be so "generated" rather than be proscribed by the "type designer"...

That too is all about your type system.

You are describing how your type system works.

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 Paul Vernon on November 17, 2021, 10:16 am
Quote from Dave Voorhis on November 17, 2021, 9:30 am.

My point is that your considerations here -- the fundamental rules you're establishing -- are inescapably and unavoidably typeful.  You are defining your type system.

OK, that is fine. Let me check something.

Given (placeholders for) values p and q, types X, Y, Z and operator Op and a:b to mean "value a at type b"

Is "value a at type b" different from "value a is of type b"?

Describing a value as "at" a type is rather nuanced and somewhat unconventional.

Then in "my system", I would never allow

p:X Op q:Y

to ever return a different result than

p:Z Op q:Y

In other words, I see no need to ever know or care what type a value is at.

Except... Every example you've given to show how you intend expressions to be evaluated is fundamentally about the types of your values.

You may not have manifest type declarations -- you might not have to declare that (say) variable p is of type Z, and that's reasonable -- but your values clearly belong to a type nonetheless, and their types affects (per the examples we've seen) their results.  E.g.:

2 + 3 evaluates to 5

"Dave" + 3 evaluates as empty (semantically "there is no result")

In other words (and under one interpretation), when both operands are (say) of type numeric, you return a numeric result. When one operand is of type numeric and the other is type string, you return empty.  Very typeful, that.

No operator will ever give a different result for the same input values just because of the type that they are "currently carrying with them".

Perhaps you mean is your type system is not polymorphic?

But "same input values" isn't compatible with differing types, because you can't have two values that are the same but of a different type. If they are different types, they are not the same.

However, you can have the same literal that denotes two or more values of different types, though. E.g., in many languages 2 can denote an integer or a float (and possibly other types), typically depending on context.

Hence I have no need for the "value at type" concept (which, BTW is one reason why I don't think that I really have a type system, or at least, no more of a type system than is logically necessary - which, as far as I can see, is not very much of one).


or do you (or others) have a counter example of a given operator that has to (or at least, where it would be very highly desirable that it does) return a different result solely due to the type(s) that its input values are at?

There is no has to in type systems, aside from what is fundamentally dictated by mathematical logic. For example, it would be illogical to define your addition operator to return 7 whenever both operands are negative. There's nothing that says you can't do it, though.

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 Darren Duncan on November 17, 2021, 2:32 am
Quote from Paul Vernon on November 15, 2021, 4:05 pm

Why, in that case, in all programming languages (that I am aware of at least) do function/operators only return one result?

I would go further than this.  Not only does every function / read-only operator return exactly one result, but it also takes exactly one argument.

Just as N conceptual outputs, where N can be zero, can be represented by a single collection value, the M conceptual inputs can also be represented by a single collection value.

In some programming languages, the concept of exactly 1 true argument is represented directly, frequently as a single lexical array named "args" or such.

Thank you Darren.

The "asymmetry" of relational operators has bothered me a little for a while. The N-adtic chapter 15 of DE is really great, but certainly we end up with expressions that are always "reducing". Going down tree like from multiple branches, reducing to one "out" from each sub expression to a single result trunk at the end.

When you then want to consider recursion, you are stuck with one input as you need to consume the previous output each time, and that is only one result.

There is also the "minefield" of when is an input or output really one, or really many. I.e. syntactically "args" is one thing, and in e.g. JavaScript is a single array containing 0-M input parameters. Is that one thing syntactically, but many things semantically? (I would say yes) . Same for output of an array or list (or indeed set)..  syntactically it is one output, but semantically many?

In the relational world we would really want any "args" thing to be a set not an array or list (because? Well Codd and Essentiality). However that has two problems. One is non-associative operators - if the input is a set of inputs, then you end up having to number them 1st input, 2nd input...  etc (and yes, you can do that in a set, but it just feels awkward resorting to tuples such as  { "Parameter Number" 1, "Parameter value" x } (and the type folks sometime struggle with the "what is the type of x" - although hence alpha) . The other problem is supporting 2 + 2. The input can't be the set {2, 2} as that is not a set.. so again one needs to number the input, or resort to bag's as input (for associative operators)...  which are worse than arrays or lists, or sets of ordered/named tuples right??

So, do I have an answer? Well, I don't think I do. I do feel that the answer should be either "1 in, 1 out" or "0..M in, 0..M" out.   The asymmetric "0..M in, 1 out" just feels like it is not the right answer. Personally I don't like "1 out" because of my thoughts in my first post of this topic. Semantically at least, you should be able to have no result or more than one result from (many) operators.

Quote from Paul Vernon on November 15, 2021, 4:05 pm

Over on another thread, Anthony said an important thing

In a programming language, a function/operator can return only one result. That result might be a pair or data structure with identifiable fields or components such that we might say loosely "more than one value". From the point of view of programming language semantics, that's one result at one type.   (Edit, I originally mis-corrected Anthony to "result and one type" as I thought it was a typo in the original,  I've now reverted my "correction")

Sure. This is how things work. But is it how things should work?

The answer to the question "What is the square root of 1", is not one answer but two.

Who wrote the third manifesto books? That has one answer, the set { "Chris Date", "Hugh Darwen" }

but the "square root of 1" is not the set {-1, 1}. No. It is -1 and it is 1.  Two independent answers.  Sure we can collect them into a set called "the square roots of 1", but that set is not the answer to the question. It is the answer to the question, "what is the set of the square roots of 1"

Take another example. What is 1 + 1?  The answer is 2 . It is not {2}. That would be the answer to the question "what is the set of the results of 1+1"

A quadratic equation has two solutions. Yes you can put those solutions into a set, but fundamentally, the solution to a quadratic equation is two things, not one.

The same goes for empty. Empty is not a value, it is the absence of value. If the result is empty, there is not one result, there is no result.

Why, in that case, in all programming languages (that I am aware of at least) do function/operators only return one result?

I appreciate you questioning things because often progress can only be made by throwing out ossified thoughts.

It doesn't work like that in all languages, but probably in most, because of the "natural" way of implementing a function call. When you need to, you work around the limitations by returning a sum type of some kind, or just a list. So a return value could for example be "Some(value)" or "None" (in Rust).

There exists the concept of coroutines that don't normally "return", they simply yield execution to some other coroutine.

In streams processing you can have each processing stage emit a stream of zero or more values into the result stream, e.g. as in the "flatMap" transform in Java streams, or transducers in Clojure and other LISPs. (Tailspin also works this way)

In the actor model, I don't think you have return values generally, the actor would have to "respond" (really act) by sending zero or more values to a specified return adress, or perhaps really zero or more messages to the return adress (or send any messages anywhere else as it is programmed to).

To get back to your questioning of types, there are many ways of defining type systems. In TTM, one way is prescribed if you are interested in creating a "D" language. If you're not interested in creating a "D", you can probably proceed with any kind of type system you like. My prejudice says that Date&Darwen are not type system experts and the type system in TTM is closely modelled on mainstream OO-languages of the time, which may not be the type system you want. Then again, Dependent types and Linear types are perhaps not what you want for data either, as those things embody constraints that are useful for programmers to maintain and (partially) prove correctness of the program. From discussions, you may just want everything to be encoded as a json-value and create sets of allowed values for each attribute (alternatively constraints defining set membership).

One thing is important, IMO, and that is the structural type system that tuples conform to. I'm not sure you can get away from that and keep a sane relational model.

Quote from AntC on November 17, 2021, 5:37 am
Quote from Paul Vernon on November 16, 2021, 10:48 pm

...

"Dave" + 3 evaluates as empty (semantically "there is no result")

2 + empty evaluates as empty (semantically "there is no result")

My two-and-a-half value logic detectors are blinking.

Thank you. I appreciate your detectors. Mine sometimes go on the blink.

What is

empty == empty -- presumably empty, because you seem to be saying any operator with an argument empty must return empty. Or True? False? Why?

Yes. The "eval-equal" operator (which most people, including me, normally call just "equal", but that actually evaluates each side that is an expression first, and then compares the results) relation does not have an entry for empty as empty is not a value as such.

("Dave" + 3) == ("Dave" + 3) then doesn't return True?

Indeed, "eval-equal" evaluates both sides, so the above first resolves to empty == empty which itself resolves to empty

So you allow expressions whose value that doesn't equal itself? ok now:

IF ("Dave" + 3) == ("Dave" + 3) THEN x := 3 ELSE x := 7;

You've got to assign a value to x.

Well, as you might have guessed. I also reject (to some degree) one other of Date and Darwen's foundation concepts (of operator, type, value and variable). I reject the idea that there is more than one (or, indeed less than one) variable. There is a variable and it is the database.

So, no I don't gotta "assign a value to x".  😀

The expression you describe above would need to be recast as a database UPDATE operator. The function part is just the set of (math) tuples { <true,3>,<false,7> } applying empty to that function will return no result - i.e. we get empty out. If you then feed that into UPDATE then, oh dear, your database is now also empty , but likely you had at least one constraint in your database and that would have stopped the update operator from doing the equivalent of a rm -rf *. (ignoring for a moment the problem of if your user is a DBA and has permission to drop all constraints via the empty update...   )

Or are you going to crash the program?

Exceptions are also something I reject. 😀

...

In general any "overloading" of operators/functions should be done with due regard to cautious design.

Seems to me you've ended up in exactly the same place as SQL: the dustbin of stupid design.

Please do shoot me if I end up in exactly the same place as SQL

Quote from Dave Voorhis on November 17, 2021, 10:59 am

You may not have manifest type declarations -- you might not have to declare that (say) variable p is of type Z, and that's reasonable

That is what I have. In "my system", you do not have to declare that (say) variable p is of type Z. Indeed you cannot declare that variable p is of type Z short of adding p to the set of values named Z.

Perhaps you mean is your type system is not polymorphic?

Which Polymorphism?  https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Types

But "same input values" isn't compatible with differing types, because you can't have two values that are the same but of a different type.

Absolutely. Indeed it is stronger than that. Two things that are the same can have no differences. That is the definition of same.

If they are different types, they are not the same.

and sub-typing? If prime is a sub-type of real  then is 2:prime (2 at prime, which Anthony thinks helps avoid confusions) is that identical (aka the same) as 2:real ?   (P.S. I'm not sure I want to know the answer - unless it will enlighten me - as in my system "value at type" is not a thing (and "value of type" is only "sort of" a thing) , so this question does not make any sense in "my system')

However, you can have the same literal that denotes two or more values of different types, though.

So "my system" very very strongly rejects that idea.  I see it as a very minor user convenience that ends up driving a wrecking ball through one's whole model. A 5 is a 5 is a 5 no matter where it is, or how it is used. Which is yet another reason not to think that 5 implicitly carries with it its type(s).

E.g., in many languages 2 can denote an integer or a float (and possibly other types), typically depending on context.

A "float" is not a thing in "my system".

OK, well, one could construct something from the set of numbers that the IEEE spec supports and add in the flags etc and call that a float..  and such a type could be made available to those who "know what they are doing", but in general, for "business users" it would not be a thing - apart, maybe, from when importing data from external systems.

If I did have such a set of values. 2 would never ever denote one. I would use some unique denotation for the thing that is a float that is constructed from the value 2 and false for the IEEE flags and whatever else is needed to fully capture the exposed information content of float values. 2e0 might even be the chosen denotation of such a thing.

 

Quote from Paul Vernon on November 17, 2021, 11:57 am
Quote from Dave Voorhis on November 17, 2021, 10:59 am

You may not have manifest type declarations -- you might not have to declare that (say) variable p is of type Z, and that's reasonable

That is what I have. In "my system", you do not have to declare that (say) variable p is of type Z. Indeed you cannot declare that variable p is of type Z short of adding p to the set of values named Z.

Perhaps you mean is your type system is not polymorphic?

Which Polymorphism?  https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Types

Any.

But "same input values" isn't compatible with differing types, because you can't have two values that are the same but of a different type.

Absolutely. Indeed it is stronger than that. Two things that are the same can have no differences. That is the definition of same.

If they are different types, they are not the same.

and sub-typing? If prime is a sub-type of real  then is 2:prime (2 at prime, which Anthony thinks helps avoid confusions) is that identical (aka the same) as 2:real ?   (P.S. I'm not sure I want to know the answer - unless it will enlighten me - as in my system "value at type" is not a thing (and "value of type" is only "sort of" a thing) , so this question does not make any sense in "my system')

A subtype is its supertype. In other words, the "same" at a specific level of abstraction.

However, you can have the same literal that denotes two or more values of different types, though.

So "my system" very very strongly rejects that idea.  I see it as a very minor user convenience that ends up driving a wrecking ball through one's whole model. A 5 is a 5 is a 5 no matter where it is, or how it is used. Which is yet another reason not to think that 5 implicitly carries with it its type(s).

That's fine. Some languages require that literals either explicitly or implicitly indicate the (only) type they denote.

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 Paul Vernon on November 17, 2021, 10:16 am
Quote from Dave Voorhis on November 17, 2021, 9:30 am.

My point is that your considerations here -- the fundamental rules you're establishing -- are inescapably and unavoidably typeful.  You are defining your type system.

OK, that is fine. Let me check something.

Given (placeholders for) values p and q, types X, Y, Z and operator Op and a:b to mean "value a at type b"

Then in "my system", I would never allow

p:X Op q:Y

to ever return a different result than

p:Z Op q:Y

In other words, I see no need to ever know or care what type a value is at. No operator will ever give a different result for the same input values just because of the type that they are "currently carrying with them". Hence I have no need for the "value at type" concept (which, BTW is one reason why I don't think that I really have a type system, or at least, no more of a type system than is logically necessary - which, as far as I can see, is not very much of one).

No operator can take an input value of different types. The rest of your argument falls away.

[Yes, you can fake it with overloading or inheritance or casting, but these are different operators sharing the same name.]


or do you (or others) have a counter example of a given operator that has to (or at least, where it would be very highly desirable that it does) return a different result solely due to the type(s) that its input values are at?

For the avoidance of doubt. I'm not interested in examples from current programming languages - unless you think that such an example is reflective of where it has to be the case (rather than just happenstance of that particular language design)

 

 

Andl - A New Database Language - andl.org
PreviousPage 4 of 8Next