May 19, 2005
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Derek Parnell schrieb am Thu, 19 May 2005 10:28:08 +1000:
> On Thu, 19 May 2005 03:37:18 +0200, Thomas Kuehne wrote:
>
>> Derek Parnell schrieb am Thu, 19 May 2005 09:25:53 +1000:
>
> [snip]
>>>> Is there any situation where exclusive ranges are neccesary and can't be replaced by inclusive ranges?
>>>> 
>>>
>>>    real x;
>>>    . . .
>>>    switch (x)
>>>    {
>>>        case 0.0 ... 0.9999999999:
>>>            . . .
>>>        break;
>>>        case 1.0 ... 1.9999999999:
>>>        . . .
>>>
>>>    }
>>>
>>> when what I really need is ...
>>>
>>>    if (x >= 0.0 and < 1.0)
>>>    {
>>>       . . .
>>>    } else if (x >= 1.0 and x < 2.0)
>>>    {
>>>       . . .
>>>    }
>> 
>> Floating types can't be used as switch conditionals.
>
> Hey! Neither can ranges ;-)
>
> We are brainstorming here, right? If we can consider ranges then I think we can consider switches using any type.

The problem with floating types are their rounding problems.

Rewrite of your sample in inclusive ranges.

	real x;
	...
	switch(x){
		case 0.0 ... 1-real.epsilon:
		....
		case 1.0 ... 2-real.epsilon:
		....
	}

Thomas


-----BEGIN PGP SIGNATURE-----

iD8DBQFCjENn3w+/yD4P9tIRAhT9AKCMEN/pAS8xH57tHllWHpvh1GGL5QCdG2Sf
Sx0XzxFMvxHe8oGzAX6gQmE=
=Jo3W
-----END PGP SIGNATURE-----
May 19, 2005
>>>> when what I really need is ...
>>>>
>>>>    if (x >= 0.0 and < 1.0)
>>>>    {
>>>>       . . .
>>>>    } else if (x >= 1.0 and x < 2.0)
>>>>    {
>>>>       . . .
>>>>    }
>>>
>>> Floating types can't be used as switch conditionals.
>>
>> Hey! Neither can ranges ;-)
>>
>> We are brainstorming here, right? If we can consider ranges then I think we
>> can consider switches using any type.
>
> The problem with floating types are their rounding problems.
>
> Rewrite of your sample in inclusive ranges.
>
> 	real x;
> 	...
> 	switch(x){
> 		case 0.0 ... 1-real.epsilon:
> 		....
> 		case 1.0 ... 2-real.epsilon:
> 		....
> 	}

His sample could be already wrong. When comparing floating points on equality, you mostly mean something like this:

if (x >= (0.0 - delta) && x < (1.0 - delta))

where delta will be greater than epsilon often. This is certainly a problem when dealing with floating points... i remember a program where this issue cost me almost a day for some nested if clauses (i needed some time to figure out where to add/subtract the delta).

Ciao
uwe
May 19, 2005
In article <d8ogr7.3b2.thomas-dloop@laermschleuder.kuehne.cn>, Thomas Kuehne says...
>
>
>-----BEGIN PGP SIGNED MESSAGE-----
>Hash: SHA1
>
>Derek Parnell schrieb am Thu, 19 May 2005 10:28:08 +1000:
>> On Thu, 19 May 2005 03:37:18 +0200, Thomas Kuehne wrote:
>>
>>> Derek Parnell schrieb am Thu, 19 May 2005 09:25:53 +1000:
>>
>> [snip]
>>>>> Is there any situation where exclusive ranges are neccesary and can't be replaced by inclusive ranges?
>>>>> 
>>>>
>>>>    real x;
>>>>    . . .
>>>>    switch (x)
>>>>    {
>>>>        case 0.0 ... 0.9999999999:
>>>>            . . .
>>>>        break;
>>>>        case 1.0 ... 1.9999999999:
>>>>        . . .
>>>>
>>>>    }
>>>>
>>>> when what I really need is ...
>>>>
>>>>    if (x >= 0.0 and < 1.0)
>>>>    {
>>>>       . . .
>>>>    } else if (x >= 1.0 and x < 2.0)
>>>>    {
>>>>       . . .
>>>>    }
>>> 
>>> Floating types can't be used as switch conditionals.
>>
>> Hey! Neither can ranges ;-)
>>
>> We are brainstorming here, right? If we can consider ranges then I think we can consider switches using any type.
>
>The problem with floating types are their rounding problems.
>
>Rewrite of your sample in inclusive ranges.
>
>	real x;
>	...
>	switch(x){
>		case 0.0 ... 1-real.epsilon:
>		....
>		case 1.0 ... 2-real.epsilon:
>		....
>	}
>
>Thomas
>

I like your example, Thomas; it's clear to the programmer, however the epsilon is specific to whichever floating point type you're finding ranges of.  real's epsilon won't work for double and float, and I'm sure you were aware of this. I'm also sure you meant it as a simple example demonstrating the technique.  But it got me thinking...

A syntax of inclusive and exclusive bounds should be proposed to fix the range problems of floating point types that lets the compiler choose the correct epsilon - or even better - generate simple floating point compare operations for range checking.

Basing my initial proposal on mathematical range syntax:

case (1.0 .. 2.0):  // x >  1.0 && x <  2.0
case [1.0 .. 2.0):  // x >= 1.0 && x <  2.0
case (1.0 .. 2.0]:  // x >  1.0 && x <= 2.0
case [1.0 .. 2.0]:  // x >= 1.0 && x <= 2.0

I'm sure the compiler can detect range conflicts with this inclusive/exclusive range syntax with a few simple heuristics or rules.  NB: I might have the meaning of () and [] reversed here, but I'm pretty sure this is mathematically correct.

Regards,
James Dunne
May 19, 2005
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

James Dunne schrieb am Thu, 19 May 2005 09:33:02 +0000 (UTC):
> In article <d8ogr7.3b2.thomas-dloop@laermschleuder.kuehne.cn>, Thomas Kuehne says...
>>Derek Parnell schrieb am Thu, 19 May 2005 10:28:08 +1000:
>>> On Thu, 19 May 2005 03:37:18 +0200, Thomas Kuehne wrote:
>>>
>>>> Derek Parnell schrieb am Thu, 19 May 2005 09:25:53 +1000:
>>>
>>> [snip]
>>>>>> Is there any situation where exclusive ranges are neccesary and can't be replaced by inclusive ranges?
>>>>>> 
>>>>>
>>>>>    real x;
>>>>>    . . .
>>>>>    switch (x)
>>>>>    {
>>>>>        case 0.0 ... 0.9999999999:
>>>>>            . . .
>>>>>        break;
>>>>>        case 1.0 ... 1.9999999999:
>>>>>        . . .
>>>>>
>>>>>    }
>>>>>
>>>>> when what I really need is ...
>>>>>
>>>>>    if (x >= 0.0 and < 1.0)
>>>>>    {
>>>>>       . . .
>>>>>    } else if (x >= 1.0 and x < 2.0)
>>>>>    {
>>>>>       . . .
>>>>>    }
>>>> 
>>>> Floating types can't be used as switch conditionals.
>>>
>>> Hey! Neither can ranges ;-)
>>>
>>> We are brainstorming here, right? If we can consider ranges then I think we can consider switches using any type.
>>
>>The problem with floating types are their rounding problems.
>>
>>Rewrite of your sample in inclusive ranges.
>>
>>	real x;
>>	...
>>	switch(x){
>>		case 0.0 ... 1-real.epsilon:
>>		....
>>		case 1.0 ... 2-real.epsilon:
>>		....
>>	}
>
> I like your example, Thomas; it's clear to the programmer, however the epsilon is specific to whichever floating point type you're finding ranges of.  real's epsilon won't work for double and float, and I'm sure you were aware of this. I'm also sure you meant it as a simple example demonstrating the technique.  But it got me thinking...
>
> A syntax of inclusive and exclusive bounds should be proposed to fix the range problems of floating point types that lets the compiler choose the correct epsilon - or even better - generate simple floating point compare operations for range checking.
>
> Basing my initial proposal on mathematical range syntax:
>
> case (1.0 .. 2.0):  // x >  1.0 && x <  2.0
> case [1.0 .. 2.0):  // x >= 1.0 && x <  2.0
> case (1.0 .. 2.0]:  // x >  1.0 && x <= 2.0
> case [1.0 .. 2.0]:  // x >= 1.0 && x <= 2.0
>
> I'm sure the compiler can detect range conflicts with this inclusive/exclusive range syntax with a few simple heuristics or rules.

Any syntax that uses un-paired braces raises huge complexity issues for any code parser.

	switch(x){
		case 0.0 ... 1-typeof(x).epsilon:
		....
		case 1.0 ... 2-typeof(x).epsilon:
		....
	}

missing: int/byte/short/long/char/dchar/wchar.epsilon==1


A shortcut(e.g. $E) for typeof(x).epsilon in case statements would be
nice.

Thomas


-----BEGIN PGP SIGNATURE-----

iD8DBQFCjIIK3w+/yD4P9tIRAvG3AJ9J/fD+76VcCYF/vKMM/H9qgty8MACgjDO6
o9S4/HdozZmpQh4zAkEv/PI=
=rPfq
-----END PGP SIGNATURE-----
May 19, 2005
>>int positive (int[] array) {
>>	int count (int[] a, int t) {
>>		match (array) {
>>			[]: return t; // end of array
>>
>>			e::next : // e = first element of a, next = array.ptr +1
>>				if (e > 0) count (next, t+1);
>>				else       count (next, t);
>>		}
>>	}
>>	return count (array, 0);
>>}
>>
>>
>
>
>The match statement looks scary though :)

:: would be an operator to create an array:
1 :: [5, 9, 42] would create the array [1, 5, 9, 42].

This operator would have a backward-mode to deconstruct an array into it's first array and the other elements. This is used in this match statement here.


In ML :: is a constructor to construct lists. Each constructor can be used to deconstruct an object.




May 19, 2005
In article <d8p0gb.3k5.thomas-dloop@laermschleuder.kuehne.cn>, Thomas Kuehne says...
>
>
>-----BEGIN PGP SIGNED MESSAGE-----
>Hash: SHA1
>
>James Dunne schrieb am Thu, 19 May 2005 09:33:02 +0000 (UTC):
>> In article <d8ogr7.3b2.thomas-dloop@laermschleuder.kuehne.cn>, Thomas Kuehne says...
>>>Derek Parnell schrieb am Thu, 19 May 2005 10:28:08 +1000:
>>>> On Thu, 19 May 2005 03:37:18 +0200, Thomas Kuehne wrote:
>>>>
>>>>> Derek Parnell schrieb am Thu, 19 May 2005 09:25:53 +1000:
>>>>
>>>> [snip]
>>>>>>> Is there any situation where exclusive ranges are neccesary and can't be replaced by inclusive ranges?
>>>>>>> 
>>>>>>
>>>>>>    real x;
>>>>>>    . . .
>>>>>>    switch (x)
>>>>>>    {
>>>>>>        case 0.0 ... 0.9999999999:
>>>>>>            . . .
>>>>>>        break;
>>>>>>        case 1.0 ... 1.9999999999:
>>>>>>        . . .
>>>>>>
>>>>>>    }
>>>>>>
>>>>>> when what I really need is ...
>>>>>>
>>>>>>    if (x >= 0.0 and < 1.0)
>>>>>>    {
>>>>>>       . . .
>>>>>>    } else if (x >= 1.0 and x < 2.0)
>>>>>>    {
>>>>>>       . . .
>>>>>>    }
>>>>> 
>>>>> Floating types can't be used as switch conditionals.
>>>>
>>>> Hey! Neither can ranges ;-)
>>>>
>>>> We are brainstorming here, right? If we can consider ranges then I think we can consider switches using any type.
>>>
>>>The problem with floating types are their rounding problems.
>>>
>>>Rewrite of your sample in inclusive ranges.
>>>
>>>	real x;
>>>	...
>>>	switch(x){
>>>		case 0.0 ... 1-real.epsilon:
>>>		....
>>>		case 1.0 ... 2-real.epsilon:
>>>		....
>>>	}
>>
>> I like your example, Thomas; it's clear to the programmer, however the epsilon is specific to whichever floating point type you're finding ranges of.  real's epsilon won't work for double and float, and I'm sure you were aware of this. I'm also sure you meant it as a simple example demonstrating the technique.  But it got me thinking...
>>
>> A syntax of inclusive and exclusive bounds should be proposed to fix the range problems of floating point types that lets the compiler choose the correct epsilon - or even better - generate simple floating point compare operations for range checking.
>>
>> Basing my initial proposal on mathematical range syntax:
>>
>> case (1.0 .. 2.0):  // x >  1.0 && x <  2.0
>> case [1.0 .. 2.0):  // x >= 1.0 && x <  2.0
>> case (1.0 .. 2.0]:  // x >  1.0 && x <= 2.0
>> case [1.0 .. 2.0]:  // x >= 1.0 && x <= 2.0
>>
>> I'm sure the compiler can detect range conflicts with this inclusive/exclusive range syntax with a few simple heuristics or rules.
>
>Any syntax that uses un-paired braces raises huge complexity issues for any code parser.
>

I agree.  Perhaps escaping them with \ would alleviate the parser's complexity issues with unmatched braces?  I don't think there is a \ token in D, so creating \[, \(, \], \) tokens would be trivial for this task.  Would that really look that bad?  It'd be easy to parse:

case \(1.0 .. 2.0\):  // x >  1.0 && x <  2.0
case \[1.0 .. 2.0\):  // x >= 1.0 && x <  2.0
case \(1.0 .. 2.0\]:  // x >  1.0 && x <= 2.0
case \[1.0 .. 2.0\]:  // x >= 1.0 && x <= 2.0

Any other character not used in D token parsing would be an equally likely candidate here like '#', '_', '\', or '@'.  I think that's all that're left in the single-character realm.

There'd also have to be the default range where both boundaries are inclusive when you don't specify explicit range syntax.

>	switch(x){
>		case 0.0 ... 1-typeof(x).epsilon:
>		....
>		case 1.0 ... 2-typeof(x).epsilon:
>		....
>	}
>
>missing: int/byte/short/long/char/dchar/wchar.epsilon==1
>
>
>A shortcut(e.g. $E) for typeof(x).epsilon in case statements would be
>nice.
>
>Thomas
>

I'm still a bit skeptical on the epsilon idea.  That $E would have to be aware
of your (x) type in typeof(x).espilon.  Perhaps $E(float), $E(double) would work
for what you're proposing?

Regards,
James Dunne
May 19, 2005
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

James Dunne schrieb am Thu, 19 May 2005 10:41:11 +0000 (UTC):
> In article <d8p0gb.3k5.thomas-dloop@laermschleuder.kuehne.cn>, Thomas Kuehne says...

<snip>

> Any other character not used in D token parsing would be an equally likely candidate here like '#', '_', '\', or '@'.  I think that's all that're left in the single-character realm.

# is used by "#line".

<snip>

>>	switch(x){
>>		case 0.0 ... 1-typeof(x).epsilon:
>>		....
>>		case 1.0 ... 2-typeof(x).epsilon:
>>		....
>>	}
>>
>>missing: int/byte/short/long/char/dchar/wchar.epsilon==1
>>
>>
>>A shortcut(e.g. $E) for typeof(x).epsilon in case statements would be
>>nice.
>>
>
> I'm still a bit skeptical on the epsilon idea.  That $E would have to be aware
> of your (x) type in typeof(x).espilon.  Perhaps $E(float), $E(double) would work
> for what you're proposing?

$E knows the type due to "switch(x)".



-----BEGIN PGP SIGNATURE-----

iD8DBQFCjI3q3w+/yD4P9tIRAnO7AKCDXQEVIDy7FuBLhoeL21M4txXgcQCfY9Oe
D+/BvtSR/+N0GtypcfE6HIY=
=vCb+
-----END PGP SIGNATURE-----
May 19, 2005
In D, the '~' operator would be more appropriate to construct arrays.

( Is this allowed by the way: int[] e = a ~ [1,2,3];
 with a beeing int or int[] ? )

The '::' should be reserved for deconstruction only

match (a) {
	foo::bar : // here, put code to be executed if a is not an
                   // empty array, foo would then be a variable with
                   // the first element of the array a, and bar
                   // would be the array a[1..$] (with its first
                   // element removed)
                   break: // optional

        [1,2,3]::bar : // here put code to be executed if a begins with
                       // the subarray [1,2,3], then bar would be
                       // the array a[3..$] (with the subarray removed)

        [] : // here put code if a is an "empty" array.
}

I like pattern matching. This looks OK don't you think? the equivalent with if, else, == would be more scary.

What do you think, feedback please !

See ya, gracias





Le Thu, 19 May 2005 10:12:02 +0000, Matthias Becker a écrit :

>>>int positive (int[] array) {
>>>	int count (int[] a, int t) {
>>>		match (array) {
>>>			[]: return t; // end of array
>>>
>>>			e::next : // e = first element of a, next = array.ptr +1
>>>				if (e > 0) count (next, t+1);
>>>				else       count (next, t);
>>>		}
>>>	}
>>>	return count (array, 0);
>>>}
>>>
>>>
>>
>>
>>The match statement looks scary though :)
> 
> :: would be an operator to create an array:
> 1 :: [5, 9, 42] would create the array [1, 5, 9, 42].
> 
> This operator would have a backward-mode to deconstruct an array into it's first array and the other elements. This is used in this match statement here.
> 
> 
> In ML :: is a constructor to construct lists. Each constructor can be used to deconstruct an object.
























May 19, 2005
And we could also imagine something more general:

	a::b::[3,4]::next

Match would occur if the 3th and 4th element of a would be '3' and '4'
a and b would contains the first and the second values, next would be a[4..$]

Etc.....



Le Thu, 19 May 2005 13:01:55 +0200, G.Vidal a écrit :

> In D, the '~' operator would be more appropriate to construct arrays.
> 
> ( Is this allowed by the way: int[] e = a ~ [1,2,3];
>  with a beeing int or int[] ? )
> 
> The '::' should be reserved for deconstruction only
> 
> match (a) {
> 	foo::bar : // here, put code to be executed if a is not an
>                    // empty array, foo would then be a variable with
>                    // the first element of the array a, and bar
>                    // would be the array a[1..$] (with its first
>                    // element removed)
>                    break: // optional
> 
>         [1,2,3]::bar : // here put code to be executed if a begins with
>                        // the subarray [1,2,3], then bar would be
>                        // the array a[3..$] (with the subarray removed)
> 
>         [] : // here put code if a is an "empty" array.












> I like pattern matching. This looks OK don't you think? the equivalent with if, else, == would be more scary.
> 
> What do you think, feedback please !
> 
> See ya, gracias
> 
> 
> 
> 
> 
> Le Thu, 19 May 2005 10:12:02 +0000, Matthias Becker a écrit :
> 
>>>>int positive (int[] array) {
>>>>	int count (int[] a, int t) {
>>>>		match (array) {
>>>>			[]: return t; // end of array
>>>>
>>>>			e::next : // e = first element of a, next = array.ptr +1
>>>>				if (e > 0) count (next, t+1);
>>>>				else       count (next, t);
>>>>		}
>>>>	}
>>>>	return count (array, 0);
>>>>}
>>>>
>>>>
>>>
>>>
>>>The match statement looks scary though :)
>> 
>> :: would be an operator to create an array:
>> 1 :: [5, 9, 42] would create the array [1, 5, 9, 42].
>> 
>> This operator would have a backward-mode to deconstruct an array into it's first array and the other elements. This is used in this match statement here.
>> 
>> 
>> In ML :: is a constructor to construct lists. Each constructor can be used to deconstruct an object.

May 19, 2005
>And we could also imagine something more general:
>
>	a::b::[3,4]::next
>
>Match would occur if the 3th and 4th element of a would be '3' and '4'
>a and b would contains the first and the second values, next would be a[4..$]
>
>Etc.....

Do you use pattern matching only to deconstruct arrays (or lists in caml)?

Normaly you can use it for anything and that's why pattern matching is interesting. Some SML:

# datatype tinyLanguage =
#     Value of int
#   | Add of Value * Value
#   | Sub of Value * Value
#   | Mul of Value * Value
#   | Div of Value * Value
#
#
# fun evaluate (Value x)   = x
# |   evaluate (Add (x, y) = (evaluate x) + (evaluate y)
# |   evaluate (Sub (x, y) = (evaluate x) - (evaluate y)
# |   evaluate (Mul (x, y) = (evaluate x) * (evaluate y)
# |   evaluate (Div (x, y) = (evaluate x) / (evaluate y);
#
#
# val exampleProgramm = Add (Mul (Value 2) (Value 3)) Value 4);
#
# val result = evaluate exampleProgramm;

Or simpler other things like a binaray tree with related functions

#  datatype 'a tree = Node of 'a * 'a tree * 'a tree | Leaf;
#
#  fun count (Node (_, left, right)) = 1 + count left + coutn right
#  |   count Leaf                    = 0;
..