Weird problem with case expressions

D

Daniel Schierbeck

I have no problem doing this:

if foo :bar then
...
end

Yet when I do this:

case "foo"
when bar :baz then "bur"
end

I get the following syntax error:

SyntaxError: compile error
(irb):30: parse error, unexpected tSYMBEG, expecting kDO or '{' or '('
when bar :baz then "bur"
^

Is this intentional? Having the expression span multiple lines doesn't
solve it.


Daniel
 
C

Carlos

Daniel said:
I have no problem doing this:

if foo :bar then
...
end

Yet when I do this:

case "foo"
when bar :baz then "bur"
end

I get the following syntax error:

SyntaxError: compile error
(irb):30: parse error, unexpected tSYMBEG, expecting kDO or '{' or '('
when bar :baz then "bur"
^

Is this intentional? Having the expression span multiple lines doesn't
solve it.

case/when have an "alternative syntax":

case "foo"
when bar: "bur"
end

probably this syntax is clashing with your construction.
--
 
M

Matthew Smillie

I have no problem doing this:

if foo :bar then
...
end

Which is odd, 'cause I get this error:

c = [something]
if c :foo then
puts "blah"
end
SyntaxError: compile error
(irb):3: parse error, unexpected kTHEN, expecting kEND
from (irb):5
from :0


What would you expect an expression of the form 'c :symbol' to mean
in the first place? The only parallel I can think of is some sort of
implicit concatenation like happens with strings ("foo" "bar" =>
"foobar"), but I don't think that's a generalisable operation.
Yet when I do this:

case "foo"
when bar :baz then "bur"
end

I get the following syntax error:

SyntaxError: compile error
(irb):30: parse error, unexpected tSYMBEG, expecting kDO or '{'
or '('
when bar :baz then "bur"
^

Is this intentional? Having the expression span multiple lines
doesn't solve it.

At this point it strikes me as intentional in the sense that it's not
a valid expression to begin with. Sorry if that's not the most
helpful answer; maybe further explanations might clear things up for me.

matthew smillie.
 
D

dblack

Hi --

I have no problem doing this:

if foo :bar then
...
end

Which is odd, 'cause I get this error:

c = [something]
if c :foo then
puts "blah"
end
SyntaxError: compile error
(irb):3: parse error, unexpected kTHEN, expecting kEND
from (irb):5
from :0


What would you expect an expression of the form 'c :symbol' to mean in the
first place?

I think in Daniel's example foo is a method, and :bar is a method
argument.


David

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
http://www.manning.com/black => RUBY FOR RAILS (reviewed on
Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
(e-mail address removed) => me
 
D

Daniel Schierbeck

Matthew said:
What would you expect an expression of the form 'c :symbol' to mean in
the first place?

I expect `foo :bar' to be the same as `foo:)bar)'.


Daniel
 
D

dblack

Hi --

I have no problem doing this:

if foo :bar then
...
end

Yet when I do this:

case "foo"
when bar :baz then "bur"
end

I get the following syntax error:

SyntaxError: compile error
(irb):30: parse error, unexpected tSYMBEG, expecting kDO or '{' or '('
when bar :baz then "bur"

I guess when binds more tightly than a parentheses-less method call.
You could do:

when bar:)baz) ...


David

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
http://www.manning.com/black => RUBY FOR RAILS (reviewed on
Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
(e-mail address removed) => me
 
M

Matthew Smillie

Hi --

I have no problem doing this:

if foo :bar then
...
end

Which is odd, 'cause I get this error:

c = [something]
if c :foo then
puts "blah"
end
SyntaxError: compile error
(irb):3: parse error, unexpected kTHEN, expecting kEND
from (irb):5
from :0


What would you expect an expression of the form 'c :symbol' to
mean in the first place?

I think in Daniel's example foo is a method, and :bar is a method
argument.

Ah, that would explain it. I admit the method/variable ambiguity
didn't even occur to me.

matthew smillie.
 
D

Daniel Schierbeck

Carlos said:
case/when have an "alternative syntax":

case "foo"
when bar: "bur"
end

probably this syntax is clashing with your construction.

if expressions have this syntax as well:

if foo: "bar"
elsif bar: "foo"
end


Daniel
 
D

Daniel Schierbeck

Hi --



I guess when binds more tightly than a parentheses-less method call.
You could do:

when bar:)baz) ...

Thank you for the response. Yes, it seems there is no way to get around
using parentheses. It's actually just an aesthetic problem; the method
in question has a question mark suffix, and I like to call such methods
without parentheses. Specifically, it was an attempt at an aesthetically
pleasing version of the code in "Symbols are your friend":

case "foo"
when of_type? :int then "integer"
when of_type? :str then "string"
end

Unfortunately, I'll have to write it like this:

case "foo"
when of_type?:)int) then "integer"
when of_type?:)str) then "string"
end

It doesn't really matter that much though -- I'm not even sure I like
that solution.


Cheers, and thanks for replying,
Daniel
 
A

ara.t.howard

Thank you for the response. Yes, it seems there is no way to get around using
parentheses. It's actually just an aesthetic problem; the method in question
has a question mark suffix, and I like to call such methods without
parentheses. Specifically, it was an attempt at an aesthetically pleasing
version of the code in "Symbols are your friend":

case "foo"
when of_type? :int then "integer"
when of_type? :str then "string"
end

Unfortunately, I'll have to write it like this:

case "foo"
when of_type?:)int) then "integer"
when of_type?:)str) then "string"
end

It doesn't really matter that much though -- I'm not even sure I like that
solution.


Cheers, and thanks for replying,
Daniel

you could do

class Symbol
def like?
{
:int => Integer,
:str => String,
:ary => Array,
}[self] or raise 'no type!'
end
end

then

case 'foo'
when :int.like? then 'integer'
when :str.like? then 'string'
end


althogh i personally would use

[Integer, String, Array].detect{|c| c === 'foo'}

since i can easily become a one liner, even with many classes. if you need
the string then

[Integer, String, Array].detect{|c| c === 'foo' and c.name.downcase}

regards.


-a
 
D

Daniel Schierbeck

Thank you for the response. Yes, it seems there is no way to get
around using parentheses. It's actually just an aesthetic problem; the
method in question has a question mark suffix, and I like to call such
methods without parentheses. Specifically, it was an attempt at an
aesthetically pleasing version of the code in "Symbols are your friend":

case "foo"
when of_type? :int then "integer"
when of_type? :str then "string"
end

Unfortunately, I'll have to write it like this:

case "foo"
when of_type?:)int) then "integer"
when of_type?:)str) then "string"
end

It doesn't really matter that much though -- I'm not even sure I like
that solution.


Cheers, and thanks for replying,
Daniel

you could do

class Symbol
def like?
{
:int => Integer,
:str => String,
:ary => Array,
}[self] or raise 'no type!'
end
end

then

case 'foo'
when :int.like? then 'integer'
when :str.like? then 'string'
end


althogh i personally would use

[Integer, String, Array].detect{|c| c === 'foo'}

since i can easily become a one liner, even with many classes. if you need
the string then

[Integer, String, Array].detect{|c| c === 'foo' and c.name.downcase}

That would work, but I don't think classes and types are always the
same. An ordered hash is still a hash, even if it doesn't descend from
Hash. This way, all objects that respond to #to_hash is in some way a
hash, and I can trust (to some degree) that the object returned when
calling that method implements the interface of Hash.


Cheers,
Daniel
 
A

ara.t.howard

althogh i personally would use

[Integer, String, Array].detect{|c| c === 'foo'}

since i can easily become a one liner, even with many classes. if you need
the string then

[Integer, String, Array].detect{|c| c === 'foo' and c.name.downcase}

That would work, but I don't think classes and types are always the same. An
ordered hash is still a hash, even if it doesn't descend from Hash. This
way, all objects that respond to #to_hash is in some way a hash, and I can
trust (to some degree) that the object returned when calling that method
implements the interface of Hash.

indeed. still, the technique is more compact than a case:

%w( to_int to_str to_hash to_ary ).detect{|m| 'foo'.respond_to? m}

and, for returning values

{
'to_int' => 'integer',
'to_str' => 'string',
'to_hash' => 'hash',
}.detect{|m,s| break s if 'foo'.respond_to? m}

note that this returns 'nil' when no match is found. but this isn't quite as
compact.

cheers.

-a
 
D

Daniel Schierbeck

althogh i personally would use

[Integer, String, Array].detect{|c| c === 'foo'}

since i can easily become a one liner, even with many classes. if
you need
the string then

[Integer, String, Array].detect{|c| c === 'foo' and c.name.downcase}

That would work, but I don't think classes and types are always the
same. An
ordered hash is still a hash, even if it doesn't descend from Hash. This
way, all objects that respond to #to_hash is in some way a hash, and I
can
trust (to some degree) that the object returned when calling that method
implements the interface of Hash.

indeed. still, the technique is more compact than a case:

%w( to_int to_str to_hash to_ary ).detect{|m| 'foo'.respond_to? m}

Very nice solution! It could even be written as:

%w{int str ary}.detect{|type| obj.respond_to? "to_#{type}"}

the only problem is that it violates DRY when used in case expressions:

case [:int, :str, :ary].detect{|type| obj.respond_to? "to_#{type}"}
when :int then "integer"
when :str then "string"
when :ary then "array"
end

(note that this isn't about mapping "ary" to "array" -- it's about
determining the type of an object, and behave in accordance with that.)


Cheers,
Daniel
 
D

Daniel Schierbeck

On a side note, this could be interesting:

class Object
def types
methods.grep(/^to_/).map{|type| type[3..-1].to_sym}
end
end

"foo".types #=> [:a, :sym, :s, :i, :f, :str]

and maybe also

class Object
def of_type?(type)
respond_to? "to_#{type}"
end
end

foo(obj) if obj.of_type? :bar

Though of course it's just an easier way to use #respond_to?


Cheers,
Daniel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top