# Which Result?

Quote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

`"Dave" + 3`

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

`2 + empty`

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

`2 + 3.54`

evaluates to`5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.The last example is certainly more interesting. In my conception

`3.54`

is not a number but a scaled-number. I.e. it is constructed from the number`3²⁷⁄₅₀`

and a scale (which is also a number) of`¹⁄₁₀₀`

. We can call scaled-numbers with a power of 10 scale asdecimals(and, call that set a type if we like). The question here this is, do we "overload"`+`

such that it works on values that are "not just" numbers (or do we use an operator of a different name). The reasonable answer is yes lets overload, and hence,`2 + 3.54`

could be either`5.54`

or to`5²⁷⁄₅₀`

. To help decide which, I'll use a new exampleSo a more tricky question is say

`³⁄₇ + 1.00`

which would be 1.428571 as a repeating decimal, but the scale is now a millionth and we had to underscore the repeating digits which means we no longer have just a scaled number. Not very satisfactory at all. So a decision has to be made. Either we always return the number, or we return a decimalunlessthere is no such decimal, in which case we return the number. To decide, consider that in the second case`³⁄₇ + 1.00`

would be`1³⁄₇`

and`⁴⁄₇ +`

`³⁄₇ + 1.00`

would`2.00`

but then`³⁄₇ + 1.00 + ⁴⁄₇`

would be`2`

. I.e`+`

would no longer be associative! That really would be too much to bear. Hence any`+`

operation on a number and a scaled number has to "throw away" the scale, and then just add the "number parts" of the values.In general any "overloading" of operators/functions should be done with due regard to cautious design.

BTW, I'm not saying that

`3.54`

gets "cast" to`3²⁷⁄₅₀`

before the`+`

operation. Rather I'm simply saying that the`+`

relation contains a (math) 3- tuple such as`<2, 3.54, 5²⁷⁄₅₀>`

as well as the "normal"`<2, 3²⁷⁄₅₀, 5²⁷⁄₅₀>`

tuple

OK well

`2 + 3`

evaluates to `5`

`"Dave" + 3`

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

`2 + empty`

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

`2 + 3.54`

evaluates to `5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.

The last example is certainly more interesting. In my conception `3.54`

is not a number but a scaled-number. I.e. it is constructed from the number `3²⁷⁄₅₀`

and a scale (which is also a number) of `¹⁄₁₀₀`

. We can call scaled-numbers with a power of 10 scale as *decimals* (and, call that set a type if we like). The question here this is, do we "overload" `+`

such that it works on values that are "not just" numbers (or do we use an operator of a different name). The reasonable answer is yes lets overload, and hence, `2 + 3.54`

could be either `5.54`

or to `5²⁷⁄₅₀`

. To help decide which, I'll use a new example

So a more tricky question is say `³⁄₇ + 1.00`

which would be 1.428571 as a repeating decimal, but the scale is now a millionth and we had to underscore the repeating digits which means we no longer have just a scaled number. Not very satisfactory at all. So a decision has to be made. Either we always return the number, or we return a decimal *unless *there is no such decimal, in which case we return the number. To decide, consider that in the second case `³⁄₇ + 1.00`

would be `1³⁄₇`

and `⁴⁄₇ +`

`³⁄₇ + 1.00`

would `2.00`

but then `³⁄₇ + 1.00 + ⁴⁄₇`

would be `2`

. I.e `+`

would no longer be associative! That really would be too much to bear. Hence any `+`

operation on a number and a scaled number has to "throw away" the scale, and then just add the "number parts" of the values.

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

BTW, I'm not saying that `3.54`

gets "cast" to `3²⁷⁄₅₀`

before the `+`

operation. Rather I'm simply saying that the `+`

relation contains a (math) 3- tuple such as `<2, 3.54, 5²⁷⁄₅₀>`

as well as the "normal" `<2, 3²⁷⁄₅₀, 5²⁷⁄₅₀>`

tuple

Quote from dandl on November 16, 2021, 11:34 pmAgain, my position is that types are not fundamental. Types (as a general concept) emerge later once you have first defined axiomatic atoms and sets, and start to add some useful functions that are not total on all values.

Please define your terms. Is this in the context of a programming language? If so, what do atoms and sets have to do with it, except as perhaps symbols in the grammar? What do you mean by 'add some functions'?

Again, my position is that types are not fundamental. Types (as a general concept) emerge later once you have first defined axiomatic atoms and sets, and start to add some useful functions that are not total on all values.

Please define your terms. Is this in the context of a programming language? If so, what do atoms and sets have to do with it, except as perhaps symbols in the grammar? What do you mean by 'add some functions'?

Quote from Darren Duncan on November 17, 2021, 2:32 amQuote from Paul Vernon on November 15, 2021, 4:05 pmWhy, in that case, in all programming languages (that I am aware of at least) do function/operators only return

oneresult?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.

Quote from Paul Vernon on November 15, 2021, 4:05 pmWhy, in that case, in all programming languages (that I am aware of at least) do function/operators only return

oneresult?

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.

Quote from AntC on November 17, 2021, 5:37 amQuote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

`"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. 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?

`("Dave" + 3) == ("Dave" + 3)`

then doesn't return`True`

?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`

. Or are you going to crash the program?...

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.

Quote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

`"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. 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?

`("Dave" + 3) == ("Dave" + 3)`

then doesn't return `True`

?

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`

. Or are you going to crash the program?

...

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

Quote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 amQuote from Paul Vernon on November 15, 2021, 4:05 pmWhy, in that case, in all programming languages (that I am aware of at least) do function/operators only return

oneresult?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.

Can't agree. Programming languages are free to take as many or as few arguments as they like. In some languages it is only one, but that argument is a list or tuple and is easily treated as many. In a few (like Haskell) the number is essentially one, but additional arguments are easily added by currying. IOW all of these are possible, and are treated as one function 3 arguments:

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))
Likewise a function must return at least one argument. In some languages the return argument is a list/tuple and the syntactic form allows that to be easily 'spread' into multiple destinations. IOW:

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

Quote from Darren Duncan on November 17, 2021, 2:32 amQuote from Paul Vernon on November 15, 2021, 4:05 pmoneresult?

Can't agree. Programming languages are free to take as many or as few arguments as they like. In some languages it is only one, but that argument is a list or tuple and is easily treated as many. In a few (like Haskell) the number is essentially one, but additional arguments are easily added by currying. IOW all of these are possible, and are treated as one function 3 arguments:

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

Likewise a function must return at least one argument. In some languages the return argument is a list/tuple and the syntactic form allows that to be easily 'spread' into multiple destinations. IOW:

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()

YMMV obviously.

Quote from dandl on November 17, 2021, 6:22 amQuote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

In some languages, not all. There are perfectly reasonable languages in which it evaluates to 23 or (+ 2 3).

`"Dave" + 3`

evaluates as empty (semantically "there is no result")No. 'empty' is not a value. There are languages in which the result is Dave3 or 3.

`2 + empty`

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

`2 + 3.54`

evaluates to`5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.The last example is certainly more interesting. In my conception

`3.54`

is not a number but a scaled-number. I.e. it is constructed from the number`3²⁷⁄₅₀`

and a scale (which is also a number) of`¹⁄₁₀₀`

. We can call scaled-numbers with a power of 10 scale asdecimals(and, call that set a type if we like). The question here this is, do we "overload"`+`

such that it works on values that are "not just" numbers (or do we use an operator of a different name). The reasonable answer is yes lets overload, and hence,`2 + 3.54`

could be either`5.54`

or to`5²⁷⁄₅₀`

. To help decide which, I'll use a new exampleBy following this line of reasoning you are relying on choosing a particular type for those values. Given other types and operators values such as 5.54, 23.54 and 5.539999999 are equally possible.

So a more tricky question is say

`³⁄₇ + 1.00`

which would be 1.428571 as a repeating decimal, but the scale is now a millionth and we had to underscore the repeating digits which means we no longer have just a scaled number. Not very satisfactory at all. So a decision has to be made. Either we always return the number, or we return a decimalunlessthere is no such decimal, in which case we return the number. To decide, consider that in the second case`³⁄₇ + 1.00`

would be`1³⁄₇`

and`⁴⁄₇ +`

`³⁄₇ + 1.00`

would`2.00`

but then`³⁄₇ + 1.00 + ⁴⁄₇`

would be`2`

. I.e`+`

would no longer be associative! That really would be too much to bear. Hence any`+`

operation on a number and a scaled number has to "throw away" the scale, and then just add the "number parts" of the values.BTW, I'm not saying that

`3.54`

gets "cast" to`3²⁷⁄₅₀`

before the`+`

operation. Rather I'm simply saying that the`+`

relation contains a (math) 3- tuple such as`<2, 3.54, 5²⁷⁄₅₀>`

as well as the "normal"`<2, 3²⁷⁄₅₀, 5²⁷⁄₅₀>`

tupleDefine your type system and the rest is just trivial calculation. Without that, it borders on meaningless.

Continuing to argue along these lines without defining your terms is an invitation to more of the same kind of response. Not productive on either side.

Quote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

In some languages, not all. There are perfectly reasonable languages in which it evaluates to 23 or (+ 2 3).

`"Dave" + 3`

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

No. 'empty' is not a value. There are languages in which the result is Dave3 or 3.

`2 + empty`

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

`2 + 3.54`

evaluates to`5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.`3.54`

is not a number but a scaled-number. I.e. it is constructed from the number`3²⁷⁄₅₀`

and a scale (which is also a number) of`¹⁄₁₀₀`

. We can call scaled-numbers with a power of 10 scale asdecimals(and, call that set a type if we like). The question here this is, do we "overload"`+`

such that it works on values that are "not just" numbers (or do we use an operator of a different name). The reasonable answer is yes lets overload, and hence,`2 + 3.54`

could be either`5.54`

or to`5²⁷⁄₅₀`

. To help decide which, I'll use a new example

By following this line of reasoning you are relying on choosing a particular type for those values. Given other types and operators values such as 5.54, 23.54 and 5.539999999 are equally possible.

`³⁄₇ + 1.00`

which would be 1.428571 as a repeating decimal, but the scale is now a millionth and we had to underscore the repeating digits which means we no longer have just a scaled number. Not very satisfactory at all. So a decision has to be made. Either we always return the number, or we return a decimalunlessthere is no such decimal, in which case we return the number. To decide, consider that in the second case`³⁄₇ + 1.00`

would be`1³⁄₇`

and`⁴⁄₇ +`

`³⁄₇ + 1.00`

would`2.00`

but then`³⁄₇ + 1.00 + ⁴⁄₇`

would be`2`

. I.e`+`

would no longer be associative! That really would be too much to bear. Hence any`+`

operation on a number and a scaled number has to "throw away" the scale, and then just add the "number parts" of the values.`3.54`

gets "cast" to`3²⁷⁄₅₀`

before the`+`

operation. Rather I'm simply saying that the`+`

relation contains a (math) 3- tuple such as`<2, 3.54, 5²⁷⁄₅₀>`

as well as the "normal"`<2, 3²⁷⁄₅₀, 5²⁷⁄₅₀>`

tuple

Define your type system and the rest is just trivial calculation. Without that, it borders on meaningless.

Continuing to argue along these lines without defining your terms is an invitation to more of the same kind of response. Not productive on either side.

Quote from Darren Duncan on November 17, 2021, 8:31 amQuote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 amCan't agree. Programming languages are free to take as many or as few arguments as they like. In some languages it is only one, but that argument is a list or tuple and is easily treated as many. In a few (like Haskell) the number is essentially one, but additional arguments are easily added by currying. IOW all of these are possible, and are treated as one function 3 arguments:

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))
Likewise a function must return at least one argument. In some languages the return argument is a list/tuple and the syntactic form allows that to be easily 'spread' into multiple destinations. IOW:

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

I'm saying that in an abstract sense every function operates by mapping a single input, the true argument, to a single output, the true result.

When someone writes `fn(a,b,c)` they are selecting a collection value of 3 elements and passing it as input to fn().

Inside fn(), some programming languages implicitly extract the 3 elements into 3 lexicals, and some programming languages alternately just provide a single lexical and the fn() code explicitly pulls out the elements.

Again, I am speaking abstractly, which provides a simplified concept, 1 to 1, which can easily emulate other concepts.

Quote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 am

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

I'm saying that in an abstract sense every function operates by mapping a single input, the true argument, to a single output, the true result.

When someone writes `fn(a,b,c)` they are selecting a collection value of 3 elements and passing it as input to fn().

Inside fn(), some programming languages implicitly extract the 3 elements into 3 lexicals, and some programming languages alternately just provide a single lexical and the fn() code explicitly pulls out the elements.

Again, I am speaking abstractly, which provides a simplified concept, 1 to 1, which can easily emulate other concepts.

Quote from Dave Voorhis on November 17, 2021, 9:21 amQuote from Darren Duncan on November 17, 2021, 8:31 amQuote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 am

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

I'm saying that in an abstract sense every function operates by mapping a single input, the true argument, to a single output, the true result.

When someone writes `fn(a,b,c)` they are selecting a collection value of 3 elements and passing it as input to fn().

Inside fn(), some programming languages implicitly extract the 3 elements into 3 lexicals, and some programming languages alternately just provide a single lexical and the fn() code explicitly pulls out the elements.

Again, I am speaking abstractly, which provides a simplified concept, 1 to 1, which can easily emulate other concepts.

I think the term you're looking for is "currying": https://en.wikipedia.org/wiki/Currying

Quote from Darren Duncan on November 17, 2021, 8:31 amQuote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 am

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

I think the term you're looking for is "currying": https://en.wikipedia.org/wiki/Currying

*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 November 17, 2021, 9:30 amQuote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

`"Dave" + 3`

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

`2 + empty`

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

`2 + 3.54`

evaluates to`5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.The specifics don't matter here -- they're up to you, and hopefully some reasonable and considered (and logical) design. The important thing is that your considerations are fundamentally typeful. Abstracted slightly so that you don't have to treat each value as a special case, you're really saying:

`<integer> + <integer>`

evaluates to`<integer>`

`<string> + <integer>`

evaluates as`null`

(semantically "there is no result")

`<integer> + <null>`

evaluates as`null`

(semantically "there is no result")

`<integer> + <decimal>`

evaluates to`<rational>`

and I will explain why that is the only reasonable result.Etc. Indeed, you can abstract it a bit further:

`<integer> <operator> <integer>`

evaluates to`<integer>`

`<string> <operator> <integer>`

evaluates as`null`

(semantically "there is no result")

`<integer> <operator> null`

evaluates as`null`

(semantically "there is no result")

`<integer> <operator> <decimal>`

evaluates to`<rational>`

and I will explain why that is the only reasonable result.There may be good reasons to avoid

`null`

(its existence in SQL and most programming languages is a source of epic problems) but you may choose to use it. That's up to you.My point is that your considerations here -- the fundamental rules you're establishing -- are inescapably and unavoidably typeful. You are defining your type system.

Quote from Paul Vernon on November 16, 2021, 10:48 pmOK well

`2 + 3`

evaluates to`5`

`"Dave" + 3`

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

`2 + empty`

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

`2 + 3.54`

evaluates to`5²⁷⁄₅₀`

and I will explain why that is the only reasonable result.

The specifics don't matter here -- they're up to you, and hopefully some reasonable and considered (and logical) design. The important thing is that your considerations are fundamentally typeful. Abstracted slightly so that you don't have to treat each value as a special case, you're really saying:

`<integer> + <integer>`

evaluates to`<integer>`

`<string> + <integer>`

evaluates as`null`

(semantically "there is no result")

`<integer> + <null>`

evaluates as`null`

(semantically "there is no result")

`<integer> + <decimal>`

evaluates to`<rational>`

and I will explain why that is the only reasonable result.

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

`<integer> <operator> <integer>`

evaluates to`<integer>`

`<string> <operator> <integer>`

evaluates as`null`

(semantically "there is no result")

`<integer> <operator> null`

evaluates as`null`

(semantically "there is no result")

`<integer> <operator> <decimal>`

evaluates to`<rational>`

and I will explain why that is the only reasonable result.

There may be good reasons to avoid `null`

(its existence in SQL and most programming languages is a source of epic problems) but you may choose to use it. That's up to you.

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

*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 dandl on November 17, 2021, 9:53 amQuote from Darren Duncan on November 17, 2021, 8:31 amQuote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 am

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

OK, if that's what you intended then we're at least on the same page. The problem is, there is a different conceptual framework with different implications.

Your approach is to treat functions taking multiple arguments as inherently different, in that the argument is a list of values. This introduces a new type, and also cuts across the concept of a function having a domain. Languages do that, but it's not nice.

The other way to think about functions with multiple arguments is via currying (courtesy Haskell Curry -- note the name). The example I gave is:

- fn(a,fn(b,fn(c)))
No new types, and each function has a domain which is a single type. It has other benefits, but you need a language like Haskell to really make use of them.

Quote from Darren Duncan on November 17, 2021, 8:31 amQuote from dandl on November 17, 2021, 6:06 amQuote from Darren Duncan on November 17, 2021, 2:32 am

- fn(a,b,c)
- fn([a,b,c])
- fn(a,fn(b,fn(c)))

- T x = fn()
- T[] x = fn()
- T[T1 x,T2 y,T3 z] = fn()
- T1 x, T2 y, T3 z = fn()
YMMV obviously.

What you said isn't actually contradicting what I said.

OK, if that's what you intended then we're at least on the same page. The problem is, there is a different conceptual framework with different implications.

Your approach is to treat functions taking multiple arguments as inherently different, in that the argument is a list of values. This introduces a new type, and also cuts across the concept of a function having a domain. Languages do that, but it's not nice.

The other way to think about functions with multiple arguments is via currying (courtesy Haskell Curry -- note the name). The example I gave is:

- fn(a,fn(b,fn(c)))

No new types, and each function has a domain which is a single type. It has other benefits, but you need a language like Haskell to really make use of them.