Scheme's (cond ((assertion) (value)).(else (value))) statement implemented in ruby?

Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))…(else (value))) syntax for what would normally
look like this in ruby:

if (assertion 1)
value 1
elsif (assertion 2)
value 2
else
default
end

I find scheme’s cond syntax more quickly digestible since it reads
like a table.
(cond
((assertion 1) (value 1))
((assertion 2) (value 2))
(else (default)))

So I wondered if I could get a similar looking function in ruby. Here
is an attempt:

class Object
alias l lambda

def cond(tests)
#acts similar to scheme/LISP cond function using hash
#key => value rather than scheme’s (cond (assertion)(value)…
(else(value)))
tests.each_pair do |k,v|
if k.call
v.call
break
else
next
end
end
end

def end_cond; l{true}; end
end

cond l{3 < 2} => l{ puts “3 is less than 2”},
l{3 > 3} => l{ puts “3 is greater than 3”},
l{3+1 > 4} => l{ puts “4 is greater than 4”},
end_cond => l{ puts “hey, it works!”}

>> hey, it works!

I was surprised that the hash seems not to mix-up the order of the k:v
pairs like it would if the keys were strings or symbols. I suppose
because they are lambda’s, the hash doesn’t bother alphabetizing them
during for the .each_pair method. Since they stay ordered, this table-
like if;elsif;else seems to work fine. Can anyone think of any issues
to using this approach? Can it give rise to unpredicted values–due to
some behind the curtain hash key sorting? Or can anyone come up with a
cleaner syntax than a hash of lambdas for both keys and values? I know
this is not idiomatic ruby here. I am just having fun.

timr wrote:

default
end

I find scheme’s cond syntax more quickly digestible since it reads
like a table.
(cond
((assertion 1) (value 1))
((assertion 2) (value 2))
(else (default)))

What’s wrong with this?

x = 3
case
when x == 2; puts ‘two’
when x == 3; puts ‘three’
else puts ‘other’
end

Clifford H…

Good Morning,

On Thu, Nov 11, 2010 at 8:25 PM, timr [email protected] wrote:

On Nov 11, 5:51 pm, Clifford H. [email protected] wrote:

Case interrogates a single variable for its state–whether it falls in
a certain range, or is equal to such and such a value. The cond
statement is much more flexible, in that each assertion can be
completely unrelated to the other assertions–i.e. they can cover more
than the state of a single variable.

Nope, well sort of nope - you are right that it checks against a single
variable but you are missing the trick. However the default “variable”
for
case is true and there are lots of options outside of the default as
well.

As an example

x = 1
y = 2
case
when x == 2 then puts ‘x’
when y == 2 then puts ‘y’
end

That outputs a nice shiny “y”

or

x = 1
y = 2
case false
when x == 2 then puts ‘x’
when y == 2 then puts ‘y’
end

This outputs an even shiner “x”

And even nicer

x = 1
y = 2
case 2
when x then puts ‘x’
when y then puts ‘y’
end

A nice shiny ‘y’ pops up on the screen

John

On Nov 11, 5:51pm, Clifford H. [email protected] wrote:

else
What’s wrong with this?

x = 3
case
when x == 2; puts ‘two’
when x == 3; puts ‘three’
else puts ‘other’
end

Clifford H…

Case interrogates a single variable for its state–whether it falls in
a certain range, or is equal to such and such a value. The cond
statement is much more flexible, in that each assertion can be
completely unrelated to the other assertions–i.e. they can cover more
than the state of a single variable.

cond l{Store.open_time…Store.close_time === Time.now}
=>l{Store.access = true},
l{after_hours_users.include? User.name}
=>l{Store.access = true},
cond_end
=>l{Store.access = false; User.rain_check(1)}

In the above code access is given to anyone during store hours, but if
it is not during store hours and the user is listed as an
after_hours_user, they are allowed acces. If they can’t get in because
it is after hours and they are not listed as an after_hours_user, they
are denied access but given a rain_check.

You can’t do this kind of complex functionality with case, which only
inquires with regards to one variable.
Tim

On Thu, Nov 11, 2010 at 7:20 PM, timr [email protected] wrote:

default
So I wondered if I could get a similar looking function in ruby. Here
if k.call

cond l{3 < 2} => l{ puts “3 is less than 2”},
l{3 > 3} => l{ puts “3 is greater than 3”},
l{3+1 > 4} => l{ puts “4 is greater than 4”},
end_cond => l{ puts “hey, it works!”}

>> hey, it works!

How about chaining ternary operators?

3 < 2 ? puts(“3 is less than 2”) :
3 > 3 ? puts(“3 is greater than 3”) :
3+1 > 4 ? puts(“4 is greater than 4”) :
puts(“hey, it works!”)

timr wrote:

Hi Rubyists,
I love ruby, but am learning scheme and kinda like the (cond
((assertion) (value))…(else (value))) syntax

I am a little bit confused by your question. In the subject line you
are asking about a Ruby equivalent to Scheme’s ‘cond’ statement. But
AFAIK, Scheme doesn’t have statements, it only has epxressions and
definitions.

If you are looking for an equivalent of the ‘cond’ derived conditional
expression, then Ruby has something which is exactly equivalent
(modulo the guarantees about proper tail calls) to the specification
in clause 11.4.5 of the 6th Revised Report on the Algorithmic Language
Scheme: the “‘case’ expression without expression” (which is quite a
mouthful …)

case
when <test> then <expression>
when <test>
  <expression>
  <expression>
else
  <expression>
end

I find scheme’s cond syntax more quickly digestible since it reads
like a table.
(cond
((assertion 1) (value 1))
((assertion 2) (value 2))
(else (default)))

That’s equivalent (again, modulo tail context guarantees) to

case
when assertion 1 then value 1
when assertion 2 then value 2
else                  default
end
tests.each_pair do |k,v|

end

cond l{3 < 2} => l{ puts “3 is less than 2”},
l{3 > 3} => l{ puts “3 is greater than 3”},
l{3+1 > 4} => l{ puts “4 is greater than 4”},
end_cond => l{ puts “hey, it works!”}

>> hey, it works!

AFAICS, this does not behave like either Scheme’s ‘cond’ or Ruby’s
‘case’ expression without expression. In particular, it doesn’t seem
to return the value of the evaluated consequence expression. Instead,
it simply returns the hash that was passed in, which doesn’t seem
particularly useful to me.

This seems to be closer to what you are looking for:

class Object
  private

  def cond(conds)
    conds.each {|t, e| if t.is_a?(Proc) then t.() else t end and 

return e.() }
end
end

puts cond ->{  3  < 2 } => ->{ '3 is less than 2' },
          ->{  3  > 3 } => ->{ '3 is greater than 3' },
          ->{ 3+1 > 4 } => ->{ '4 is greater than 4' },
                     else: ->{ 'hey, it works!' }

I was surprised that the hash seems not to mix-up the order of the k:v
pairs like it would if the keys were strings or symbols. I suppose
because they are lambda’s, the hash doesn’t bother alphabetizing them
during for the .each_pair method. Since they stay ordered, this table-
like if;elsif;else seems to work fine. Can anyone think of any issues
to using this approach? Can it give rise to unpredicted values–due to
some behind the curtain hash key sorting?

In Ruby 1.9, hashes are always guaranteed to be ordered by insertion.
In Ruby 1.8, hashes are always unordered, regardless of whether their
keys are symbols, strings, procs or unicorns.

Or can anyone come up with a cleaner syntax than a hash of lambdas for
both keys and values?

In Ruby 1.9, I think it is just fine. In Ruby 1.8, you would have to
use something which is ordered, like an array, or you would have to
require that the branches are mutually exclusive, which of course
rules out the possiblity of an ‘else’ branch.

What I am not entirely clear about is what’s wrong with

puts case
     when  3  < 2 then '3 is less than 2'
     when  3  > 3 then '3 is greater than 3'
     when 3+1 > 4 then '4 is greater than 4'
     else              'hey, it works!'
     end

That looks just fine to me.

Another thing that bugs me is that Ruby is an object-oriented
language, but I don’t see an obvious object that the cond method
should belong to.

jwm

On Nov 12, 4:42am, Josh C. [email protected] wrote:

On Thu, Nov 11, 2010 at 7:20 PM, timr [email protected] wrote:

default
is an attempt:
v.call
cond l{3 < 2} => l{ puts “3 is less than 2”},
3+1 > 4 ? puts(“4 is greater than 4”) :
puts(“hey, it works!”)

Woa, that is slick!

On Nov 12, 6:14am, Jrg W Mittag [email protected]
wrote:

If you are looking for an equivalent of the ‘cond’ derived conditional

end
case

#key => value rather than scheme’s (cond (assertion)(value)…

AFAICS, this does not behave like either Scheme’s ‘cond’ or Ruby’s
def cond(conds)

pairs like it would if the keys were strings or symbols. I suppose
Or can anyone come up with a cleaner syntax than a hash of lambdas for
when 3 < 2 then ‘3 is less than 2’

jwm

Perhaps you didn’t notice the beginning of the code, where you find
the def for cond within the Object class.

class Object
alias l lambda
def cond(tests)
#acts similar to scheme/LISP cond function using hash
#key => value rather than scheme’s (cond (assertion)(value)…
(else(value)))
tests.each_pair do |k,v|
if k.call
v.call
break
else
next
end
end
end
def end_cond; l{true}; end
end

On Nov 11, 8:43pm, John W Higgins [email protected] wrote:

completely unrelated to the other assertions–i.e. they can cover more
case
case false
case 2
when x then puts ‘x’
when y then puts ‘y’
end

A nice shiny ‘y’ pops up on the screen

John

Oh, I see. I have always seen it in the simplest form only:
print "Enter your grade: "
grade = gets.chomp
case grade
when “A”
puts ‘Well done!’
when “B”
puts ‘Try harder!’
when “C”
puts ‘You need help!!!’
else
puts “You just making it up!”
end

Thanks. That is certainly better.
Tim

On Nov 12, 6:14am, Jrg W Mittag [email protected]
wrote:

If you are looking for an equivalent of the ‘cond’ derived conditional

end
case

#key => value rather than scheme’s (cond (assertion)(value)…

AFAICS, this does not behave like either Scheme’s ‘cond’ or Ruby’s
def cond(conds)

pairs like it would if the keys were strings or symbols. I suppose
Or can anyone come up with a cleaner syntax than a hash of lambdas for
both keys and values?

In Ruby 1.9, I think it is just fine. In Ruby 1.8, you would have to
use something which is ordered, like an array, or you would have to
require that the branches are mutually exclusive, which of course
rules out the possiblity of an ‘else’ branch.

But it runs fine for me in 1.8.7:

RUBY_VERSION
=> “1.8.7”

class Object
alias l lambda

?> def cond(tests)

#acts similar to scheme/LISP cond function using hash

?> #key => value rather than scheme’s (cond (assertion)(value)…
(else(value)))**
?> tests.each_pair do |k,v|
?> if k.call

    v.call
    break
  else

?> next

  end
end

end

?> def end_cond; l{true}; end

end
=> nil

?> cond l{3 < 2} => l{ puts “3 is greater than 2”},
?> l{3 > 3} => l{ puts “3 is greater than 3”},
?> l{3+1 > 4} => l{ puts “4 is greater than 4”},
?> end_cond => l{ puts “hey, it works!”}
hey, it works!
=> nil

Yes. I agree it is beter. I didn’t realize that case statements could
be used with this more flexible syntax. My bad.

Another thing that bugs me is that Ruby is an object-oriented
language, but I don’t see an obvious object that the cond method
should belong to.

If defined in Object class, cond could be used within almost any
context (since almost every object is an ancestor of Object).