Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > RCR draft for enhanced "case..when..else..end" syntax

Reply
Thread Tools

RCR draft for enhanced "case..when..else..end" syntax

 
 
Guoliang Cao
Guest
Posts: n/a
 
      02-03-2004
Hi,

I'm thinking of submitting a RCR. Here is the draft. Comments are welcome.

Thanks,
Cao

================================================== ==

RCR title: An enhanced case..when..else..end syntax

This RCR involves (check all that apply):
... a new feature x
... a syntax change x
... refactoring only
... backwards incompatibility

Abstract....
This RCR proposes an enhanced case..when..else..end syntax.

Problem....
It's a very common task to group code based on a combination of values of
multiple variables. However, the current Ruby syntax only allows one
expression/variable being used in "case" clause. People have to use
if..else..end which requires more typing and is less intuitive.

For example, suppose I'm generating a image, the color of each point is
defined based on its x and y coordinates.

0 ... 50...100...200
0 +----------------+
| white |
50 |----------------|
| | grey |
100 |blue +----------|
| | black |
150 |----------------|
| white |
200 +----------------+


Use if..else..end:

if 0..50 === x || 150..200 === x
color = white
elsif 0..50 === y
color = blue
elsif 50..100 === x and 50..200 === y
color = grey
else
color = black
end

Use new case..when..else..end:

case x : y
when 0..50, 150..200 # x in 0..50 or 150..200
color = white
when _ : 0..50 # y in 0..50
color = blue
when 50..100 : 50..200 # x in 50..100 and y in 50..200
color = grey
else # otherwise
color = black
end

Note: If the color is only dependent on x, we can use the current "case"
syntax. However if it's dependent on more than one variable, "case" is
incapable of handling it.

Proposal....
The enhanced syntax looks like below:

case expr1 : expr2
when expr3 : _ : expr4
do_something
when expr5
do_different_thing
else
do_default_thing
end

1. "case" takes 0 or any number of expressions separated by ":";

If there's no expression, each clause separated by ":" in "when" is
evaluated to true or false and ":" is treated the same way as "and".

2. "when" takes 1 or more clauses separated by ":"; each clause by itself
can be separated by "," (this is supported in current syntax).

If there are less clauses than the number of "case" expressions, the
rest of "case" expressions are not evaluated.

If there are more clauses than the number of "case" expressions, the
rest of "when" clauses are evaluated to true or false. ":" is treated
the same way as "and"

3. "else" remains the same.

4. "_" means skipping evaluation of the corresponding "case" expression.


Analysis....
What benefits do we get?
1. It's a more general form, which means it can be used much wider than
what "case..when" is used for today;
2. less typing; there's no need to type x, y, === in the when clause;
3. this syntax change is backward compatible with Ruby 1.x

Implementation....
Because this request proposes syntax enhancement, it can't be achieved
without changing Ruby interpreter.

The code below demonstrated the idea by extending Object class and
utilizing thread-local variables. It uses "ccase..cwhen..celse..end" to
simulate the proposed syntax. It uses "," to separate ccase/cwhen clauses
which are just arguments. It uses "true" instead of "_" to indicate
skipping evaluation.


Source:

#!/usr/bin/ruby
#
# To utilize Ruby's existing functionality to demonstrate the idea, I'm doing
# this:
# add instance methods "ccase", "cwhen", "celse" to class Object which is the
# top one in the hierarchy so all classes inherit them.
#
# Its usage is like this:
# ccase a, b do
# cwhen [1,2], 1..3 do
# ...
# end
# cwhen 3, true do
# ...
# end
# celse do
# ...
# end
# end
#
# Problems so far:
# 1. how to let "cwhen" method body to access "ccase" arguments and
# intermediate objects;
# Solution: use thread-local variable
#
# 2. how to make it work in multi-threaded program;
# Solution: use thread-local variable
#
# 3. "do...end" or "{...}" is needed to pass block to ccase/cwhen/celse.
# Solution: ???
#
# ccase = "customized case"
# cwhen = "customized when"
# celse = "customized else"
#

class Object

# customized case
def ccase arg0, *args
begin
args.unshift arg0
Thread.current["in_ccase"] = true
Thread.current["ccase_args"] = args
Thread.current["hit_cwhen"] = nil
if block_given?
yield
end
ensure
Thread.current["in_ccase"] = nil
Thread.current["ccase_args"] = nil
Thread.current["hit_cwhen"] = nil
end
end

# customized when
def cwhen arg0, *args
# skip if already matched
return if Thread.current["hit_cwhen"]

args.unshift arg0
hit = true # hit or not?
ccase_args = Thread.current["ccase_args"]

if ccase_args and ccase_args.length > 0
ccase_args.each_index do |idx|
ccase_arg = ccase_args[idx]
cwhen_arg = args[idx]

# cwhen_arg is not present, hit = true
# cwhen_arg is true and ccase_arg is not false, hit = true
# ccase_arg is not present, hit = if cwhen_arg evaluates to true
# both ccase_arg and cwhen_arg are present, hit = if cwhen_arg === ccase_arg
if (not cwhen_arg) or (not ccase_arg and cwhen_arg) or (cwhen_arg == true and not ccase_arg == false) or (cwhen_arg === ccase_arg)
# hit = true
elsif cwhen_arg.is_a? Array
hit = false
cwhen_arg.each do |arg|
if arg === ccase_arg
hit = true
break
end
end
else
hit = false
end
s = if cwhen_arg.nil? then "nil" else cwhen_arg.to_s end
s1 = if ccase_arg.nil? then "nil" else ccase_arg.to_s end
print "#{s} === #{s1} : #{hit.to_s}\n"

break if not hit
end
else
args.each {|arg| hit &= arg}
end

return if not hit
Thread.current["hit_cwhen"] = true

if block_given?
yield
end
end

# customized else to be combined with ccase/cwhen
def celse
return if Thread.current["hit_cwhen"]
if block_given?
yield
end
end

end

if __FILE__ == $0
def test a,b,c
ccase a,b,c do
cwhen [1, 3, 6], 1..2 do
# if a is in [1,3,6] and b === 1..2
print "hit 1\n"
end
cwhen 4, 3, 1 do
# if a === 4 and b === 4 and c === 1
print "hit 2\n"
end
cwhen 5, true, 7 do
# if a === 5 and c === 7
print "hit 3\n"
end
celse do
# otherwise
print "no hit\n"
end
end
end

test 1,2,3 # hit 1
test 4,3,0 # no hit
test 4,3,1 # hit 2
test 5,6,7 # hit 3

end



 
Reply With Quote
 
 
 
 
Robert Klemme
Guest
Posts: n/a
 
      02-03-2004

"Guoliang Cao" <(E-Mail Removed)> schrieb im Newsbeitrag
news:(E-Mail Removed). ..
> Hi,
>
> I'm thinking of submitting a RCR. Here is the draft. Comments are

welcome.

[snip]

Do we need this?

16:41:55 [ruby]: ./ccase.rb
[#<struct Point x=0, y=0>, "white"]
[#<struct Point x=100, y=0>, "blue"]
[#<struct Point x=55, y=55>, "grey"]
[#<struct Point x=120, y=210>, "black"]
16:41:57 [ruby]: cat ccase.rb
#!/usr/bin/ruby

Point = Struct.new( , :y )

class CondWhite
def self.===(point); (0..50) === point.x or (150..200) === point.x; end
end

class CondBlue
def self.===(point); (50..150) === point.x and (0..50) === point.y; end
end

class CondGrey
def self.===(point); (50..100) === point.x and (50..200) === point.y;
end
end


points = [
Point.new( 0, 0 ),
Point.new( 100, 0 ),
Point.new( 55, 55 ),
Point.new( 120, 210 ),
]

points.each do |point|
case point
when CondWhite; p [point, "white"]
when CondBlue; p [point, "blue"]
when CondGrey; p [point, "grey"]
else; p [point, "black"]
end
end
16:42:00 [ruby]:

Regards

robert

 
Reply With Quote
 
 
 
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      02-03-2004
Hi,

At Tue, 3 Feb 2004 23:35:25 +0900,
Guoliang Cao wrote:
> ... backwards incompatibility

?

> 2. "when" takes 1 or more clauses separated by ":"; each clause by itself
> can be separated by "," (this is supported in current syntax).


"when" clause can be terminated by ":" now. It can conflict.

> 4. "_" means skipping evaluation of the corresponding "case" expression.


It feels ugly a bit, to me.

--
Nobu Nakada


 
Reply With Quote
 
Gennady
Guest
Posts: n/a
 
      02-03-2004
Guoliang Cao wrote:
> Hi,
>
> I'm thinking of submitting a RCR. Here is the draft. Comments are welcome.
>
> Thanks,
> Cao


Do you know about this syntax currently available?

a = "aaa"
b = "bbb"
case
when a == "aaa", b == "bbb"
puts "Lowercase detected"
when a == "AAA", b == "BBB"
puts "Uppercase detected"
else
puts "Detection failed"
end

>
> ================================================== ==
>
> RCR title: An enhanced case..when..else..end syntax
>
> This RCR involves (check all that apply):
> ... a new feature x
> ... a syntax change x
> ... refactoring only
> ... backwards incompatibility
>
> Abstract....
> This RCR proposes an enhanced case..when..else..end syntax.
>
> Problem....
> It's a very common task to group code based on a combination of values of
> multiple variables. However, the current Ruby syntax only allows one
> expression/variable being used in "case" clause. People have to use
> if..else..end which requires more typing and is less intuitive.
>
> For example, suppose I'm generating a image, the color of each point is
> defined based on its x and y coordinates.
>
> 0 ... 50...100...200
> 0 +----------------+
> | white |
> 50 |----------------|
> | | grey |
> 100 |blue +----------|
> | | black |
> 150 |----------------|
> | white |
> 200 +----------------+
>
>
> Use if..else..end:
>
> if 0..50 === x || 150..200 === x
> color = white
> elsif 0..50 === y
> color = blue
> elsif 50..100 === x and 50..200 === y
> color = grey
> else
> color = black
> end
>
> Use new case..when..else..end:
>
> case x : y
> when 0..50, 150..200 # x in 0..50 or 150..200
> color = white
> when _ : 0..50 # y in 0..50
> color = blue
> when 50..100 : 50..200 # x in 50..100 and y in 50..200
> color = grey
> else # otherwise
> color = black
> end
>
> Note: If the color is only dependent on x, we can use the current "case"
> syntax. However if it's dependent on more than one variable, "case" is
> incapable of handling it.
>
> Proposal....
> The enhanced syntax looks like below:
>
> case expr1 : expr2
> when expr3 : _ : expr4
> do_something
> when expr5
> do_different_thing
> else
> do_default_thing
> end
>
> 1. "case" takes 0 or any number of expressions separated by ":";
>
> If there's no expression, each clause separated by ":" in "when" is
> evaluated to true or false and ":" is treated the same way as "and".
>
> 2. "when" takes 1 or more clauses separated by ":"; each clause by itself
> can be separated by "," (this is supported in current syntax).
>
> If there are less clauses than the number of "case" expressions, the
> rest of "case" expressions are not evaluated.
>
> If there are more clauses than the number of "case" expressions, the
> rest of "when" clauses are evaluated to true or false. ":" is treated
> the same way as "and"
>
> 3. "else" remains the same.
>
> 4. "_" means skipping evaluation of the corresponding "case" expression.
>
>
> Analysis....
> What benefits do we get?
> 1. It's a more general form, which means it can be used much wider than
> what "case..when" is used for today;
> 2. less typing; there's no need to type x, y, === in the when clause;
> 3. this syntax change is backward compatible with Ruby 1.x
>
> Implementation....
> Because this request proposes syntax enhancement, it can't be achieved
> without changing Ruby interpreter.
>
> The code below demonstrated the idea by extending Object class and
> utilizing thread-local variables. It uses "ccase..cwhen..celse..end" to
> simulate the proposed syntax. It uses "," to separate ccase/cwhen clauses
> which are just arguments. It uses "true" instead of "_" to indicate
> skipping evaluation.
>
>
> Source:
>
> #!/usr/bin/ruby
> #
> # To utilize Ruby's existing functionality to demonstrate the idea, I'm doing
> # this:
> # add instance methods "ccase", "cwhen", "celse" to class Object which is the
> # top one in the hierarchy so all classes inherit them.
> #
> # Its usage is like this:
> # ccase a, b do
> # cwhen [1,2], 1..3 do
> # ...
> # end
> # cwhen 3, true do
> # ...
> # end
> # celse do
> # ...
> # end
> # end
> #
> # Problems so far:
> # 1. how to let "cwhen" method body to access "ccase" arguments and
> # intermediate objects;
> # Solution: use thread-local variable
> #
> # 2. how to make it work in multi-threaded program;
> # Solution: use thread-local variable
> #
> # 3. "do...end" or "{...}" is needed to pass block to ccase/cwhen/celse.
> # Solution: ???
> #
> # ccase = "customized case"
> # cwhen = "customized when"
> # celse = "customized else"
> #
>
> class Object
>
> # customized case
> def ccase arg0, *args
> begin
> args.unshift arg0
> Thread.current["in_ccase"] = true
> Thread.current["ccase_args"] = args
> Thread.current["hit_cwhen"] = nil
> if block_given?
> yield
> end
> ensure
> Thread.current["in_ccase"] = nil
> Thread.current["ccase_args"] = nil
> Thread.current["hit_cwhen"] = nil
> end
> end
>
> # customized when
> def cwhen arg0, *args
> # skip if already matched
> return if Thread.current["hit_cwhen"]
>
> args.unshift arg0
> hit = true # hit or not?
> ccase_args = Thread.current["ccase_args"]
>
> if ccase_args and ccase_args.length > 0
> ccase_args.each_index do |idx|
> ccase_arg = ccase_args[idx]
> cwhen_arg = args[idx]
>
> # cwhen_arg is not present, hit = true
> # cwhen_arg is true and ccase_arg is not false, hit = true
> # ccase_arg is not present, hit = if cwhen_arg evaluates to true
> # both ccase_arg and cwhen_arg are present, hit = if cwhen_arg === ccase_arg
> if (not cwhen_arg) or (not ccase_arg and cwhen_arg) or (cwhen_arg == true and not ccase_arg == false) or (cwhen_arg === ccase_arg)
> # hit = true
> elsif cwhen_arg.is_a? Array
> hit = false
> cwhen_arg.each do |arg|
> if arg === ccase_arg
> hit = true
> break
> end
> end
> else
> hit = false
> end
> s = if cwhen_arg.nil? then "nil" else cwhen_arg.to_s end
> s1 = if ccase_arg.nil? then "nil" else ccase_arg.to_s end
> print "#{s} === #{s1} : #{hit.to_s}\n"
>
> break if not hit
> end
> else
> args.each {|arg| hit &= arg}
> end
>
> return if not hit
> Thread.current["hit_cwhen"] = true
>
> if block_given?
> yield
> end
> end
>
> # customized else to be combined with ccase/cwhen
> def celse
> return if Thread.current["hit_cwhen"]
> if block_given?
> yield
> end
> end
>
> end
>
> if __FILE__ == $0
> def test a,b,c
> ccase a,b,c do
> cwhen [1, 3, 6], 1..2 do
> # if a is in [1,3,6] and b === 1..2
> print "hit 1\n"
> end
> cwhen 4, 3, 1 do
> # if a === 4 and b === 4 and c === 1
> print "hit 2\n"
> end
> cwhen 5, true, 7 do
> # if a === 5 and c === 7
> print "hit 3\n"
> end
> celse do
> # otherwise
> print "no hit\n"
> end
> end
> end
>
> test 1,2,3 # hit 1
> test 4,3,0 # no hit
> test 4,3,1 # hit 2
> test 5,6,7 # hit 3
>
> end
>
>




 
Reply With Quote
 
Guoliang Cao
Guest
Posts: n/a
 
      02-03-2004


---- Original Message ----
> Hi,


> At Tue, 3 Feb 2004 23:35:25 +0900,
> Guoliang Cao wrote:
>> ... backwards incompatibility

> ?


>> 2. "when" takes 1 or more clauses separated by ":"; each clause by itself
>> can be separated by "," (this is supported in current syntax).


> "when" clause can be terminated by ":" now. It can conflict.


Really? In the Pragmatic Programmer's Guide, it didn't mention ":" but uses
"then":

"...and you also need a then keyword if the expression is on the same line
as the condition."

>> 4. "_" means skipping evaluation of the corresponding "case" expression.


> It feels ugly a bit, to me.


"~" or whichever looks good to most people and doesn't cause backward
incompatibility problem.

Cao



 
Reply With Quote
 
Guoliang Cao
Guest
Posts: n/a
 
      02-03-2004


---- Original Message ----
> Guoliang Cao wrote:
>> Hi,
>>
>> I'm thinking of submitting a RCR. Here is the draft. Comments are welcome.
>>
>> Thanks,
>> Cao


> Do you know about this syntax currently available?


> a = "aaa"
> b = "bbb"
> case
> when a == "aaa", b == "bbb"
> puts "Lowercase detected"
> when a == "AAA", b == "BBB"
> puts "Uppercase detected"
> else
> puts "Detection failed"
> end


No. This is close to what I want though. The only drawback is you still
have to type "a", "b" in each when clause.

Cao



 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      02-03-2004

"Gennady" <(E-Mail Removed)> schrieb im Newsbeitrag
news:(E-Mail Removed)...
> Guoliang Cao wrote:
> > Hi,
> >
> > I'm thinking of submitting a RCR. Here is the draft. Comments are

welcome.
> >
> > Thanks,
> > Cao

>
> Do you know about this syntax currently available?
>
> a = "aaa"
> b = "bbb"
> case
> when a == "aaa", b == "bbb"
> puts "Lowercase detected"
> when a == "AAA", b == "BBB"
> puts "Uppercase detected"
> else
> puts "Detection failed"
> end


Since which version of Ruby? Thx!

robert

 
Reply With Quote
 
Gennady
Guest
Posts: n/a
 
      02-03-2004
Robert Klemme wrote:
> "Gennady" <(E-Mail Removed)> schrieb im Newsbeitrag
> news:(E-Mail Removed)...
>
>>Guoliang Cao wrote:
>>
>>>Hi,
>>>
>>>I'm thinking of submitting a RCR. Here is the draft. Comments are

>
> welcome.
>
>>>Thanks,
>>>Cao

>>
>>Do you know about this syntax currently available?
>>
>>a = "aaa"
>>b = "bbb"
>>case
>> when a == "aaa", b == "bbb"
>> puts "Lowercase detected"
>> when a == "AAA", b == "BBB"
>> puts "Uppercase detected"
>> else
>> puts "Detection failed"
>>end

>
>
> Since which version of Ruby? Thx!
>
> robert
>
>


I checked that it works in 1.6.8. I remember somebody (Nobu?) mentioning
it on ruby-talk some time ago.

Gennady.


 
Reply With Quote
 
Guoliang Cao
Guest
Posts: n/a
 
      02-03-2004


---- Original Message ----


> ---- Original Message ----
>> Hi,


>> At Tue, 3 Feb 2004 23:35:25 +0900,
>> Guoliang Cao wrote:
>>> ... backwards incompatibility

>> ?


>>> 2. "when" takes 1 or more clauses separated by ":"; each clause by itself
>>> can be separated by "," (this is supported in current syntax).


>> "when" clause can be terminated by ":" now. It can conflict.


> Really? In the Pragmatic Programmer's Guide, it didn't mention ":" but uses
> "then":


> "...and you also need a then keyword if the expression is on the same line
> as the condition."


Just tried on Ruby 1.6.8. It uses "then" instead of ":". So there's no
backward incompatibility.

Cao


>>> 4. "_" means skipping evaluation of the corresponding "case" expression.


>> It feels ugly a bit, to me.


> "~" or whichever looks good to most people and doesn't cause backward
> incompatibility problem.


> Cao





 
Reply With Quote
 
nobu.nokada@softhome.net
Guest
Posts: n/a
 
      02-03-2004
Hi,

At Wed, 4 Feb 2004 03:11:35 +0900,
Guoliang Cao wrote:
> Just tried on Ruby 1.6.8. It uses "then" instead of ":". So there's no
> backward incompatibility.


$ ruby-1.8 -e 'if /^\w+/ =~ "abc": puts $& end'
abc
$ ruby-1.8 -e 'case "abc" when /^\w+/: puts $& end'
abc
$ ruby-1.8 -e 'while l = gets: p l end'
abc
"abc\n"

Now ":" can be placed anywhere instead of "then", and "do" for
loops.

--
Nobu Nakada


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
RCR? Added syntax for chains that possibly return nil Stefan Rusterholz Ruby 34 07-21-2007 08:15 AM
Cisco 2950 with enhanced software question/problem Stohon Cisco 6 03-15-2006 09:12 AM
Ip phones - enhanced 128k AAC-LD quality R Siffredi Cisco 1 02-08-2005 05:19 AM
Enhanced Tera Term...very nice. Hansang Bae Cisco 2 01-28-2005 05:29 AM
PEP-0315--Enhanced While Loop: An idea for an alternative syntax Andrew Koenig Python 46 02-24-2004 07:14 PM



Advertisments