Just some Ruby language ideas, comments wanted.

D

Daniel Finnie

My first Ruby language idea is simple. Instead of having every method
take a default value, do the following:
class Object
def default(*args)
self
end
end

class NilClass
def default(arg)
arg # And have a block form, too.
end
end

How this would work:
Currently, the following would default to "i" if the element doesn't exist.
[4, 6, 9].fetch(4, "i")
This would do the same thing:
[4, 6, 9][4].default("i") or [4, 6, 9].fetch(4).default("i")

It would reduce a lot of repeated code in the actual Ruby codebase and
would give more room for parameters to functions (because we don't
really have named parameters).

Plus, the Ruby Way is not to do:
retVal = defaultValue if retVal.nil?

And this would solve that.

-------

My second idea is a lot more out there but, I feel, still a good idea.
A form of all the classes with Enumerable mixed in (hash, array, etc)
where all the methods like select return not the values that were
selected but an object that contains a reference back to the index of
the array where it was stored.

This would allow things like:
ary = [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # => [4, nil, 6, 2]

As it is now, the above wouldn't work because select returns an array of
values from the array, so calling set only changes the value in the
array that was created by set.

With my idea, select would return an "ArrayValue" (or something) class,
and the set parameter of that class would update the original array.

The ArrayValue class could be implemented as such:
class ArrayValue
def initialize(origArray, origIndex)
@origArray, @origIndex = origArray, origIndex
end

def get
@origArray[@origIndex]
end

def set(newObj)
@origArray[@origIndex] = newObj
end

# more methods, like delete, etc could be implemented.
end

There are some problems to this right now, mainly:
-Have to use a method to set, not the un-overloadable = lanugage construct.
-Need some disambiguation between methods that return values and methods
that return ArrayValues (maybe everything returns an ArrayValue and one
must use ArrayValue#get).
-Fundamental Redesign

Despite these problems, I see a lot of use cases for ArrayValue.

Thanks for reading (that long thing),
Dan
 
D

Devin Mullins

Daniel said:
How this would work:
Currently, the following would default to "i" if the element doesn't exist.
[4, 6, 9].fetch(4, "i")
This would do the same thing:
[4, 6, 9][4].default("i") or [4, 6, 9].fetch(4).default("i")
I get along fine with:
[4, 6, 9].fetch(4)||'i'

Sure, it has false misses on instances of FalseClass, but I live with
the pain. Besides, what if the above array contained nil?
This would allow things like:
ary = [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # => [4, nil, 6, 2]
May I suggest:
ary.select{|x| x.nil?}[1] = 2
as a better interface.

= isn't overridable, but []= is.

I see more potential in your second thing... Build it as a stand-alone
Ruby library first.

Devin
 
D

David Vallner

--------------enig3C14B38F3E15D4FFBB7E4009
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Devin said:
This would allow things like:
ary =3D [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # =3D> [4, nil, 6, 2]
May I suggest:
ary.select{|x| x.nil?}[1] =3D 2
as a better interface.
=20

This wouldn't work with the current semantics of select, since select
returns a filtered copy of the array.

I'm not sure I'd like if select returned some subclass of array that
overrides []=3D to propagate changes back to the original one. First and
foremost, potential massive code breakage without solving an actual use
case. Secondly, it's emulating poor man's variable references that
doesn't quite fit into Ruby. (Overall I don't see the use of this sort
of collection view pattern much.)

Maybe as part of Enumerator or Facets or a similar Library Of Somewhat
Generic Spiffy Things, but not in the standard library, and definately
not in a codebreaking way.

David Vallner


--------------enig3C14B38F3E15D4FFBB7E4009
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFhETiy6MhrS8astoRAsY6AJ9fx5ZvK2/bBI/ojS4HBlhqnbyLHgCeKwUE
4XFrgkLa2UlZmNfeCoKtdV8=
=0lhn
-----END PGP SIGNATURE-----

--------------enig3C14B38F3E15D4FFBB7E4009--
 
J

James Edward Gray II

Maybe as part of Enumerator or Facets or a similar Library Of Somewhat
Generic Spiffy Things, but not in the standard library...

I'm not sure what you are saying here. Enumerator is a standard
library is Ruby 1.8 and promoted to the language core in Ruby 1.9.

James Edward Gray II
 
D

David Vallner

--------------enig3F0ADA0522A28EB1019A24B7
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Dec 16, 2006, at 1:11 PM, David Vallner wrote:
=20
=20
I'm not sure what you are saying here. Enumerator is a standard librar= y
is Ruby 1.8 and promoted to the language core in Ruby 1.9.
=20

Yes, but it is a library. I admit my phrasing was wrong, I just wouldn't
like the "collection view" features Enumerator might or might not have
be the default behaviour for #select and the like.

David Vallner


--------------enig3F0ADA0522A28EB1019A24B7
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFhFvJy6MhrS8astoRAgMjAJ97npLo7FwfwomrXpepR0TWP2oK7QCfWo2I
mDQ8d5Ih9ixDqhudUmMxLR8=
=Orwi
-----END PGP SIGNATURE-----

--------------enig3F0ADA0522A28EB1019A24B7--
 
D

Devin Mullins

David said:
Devin said:
May I suggest:
ary.select{|x| x.nil?}[1] = 2
as a better interface.

This wouldn't work with the current semantics of select, since select
returns a filtered copy of the array.

Yeah, my bad, lemme re-suggest something like:
ary.as_references.select {|x| x.nil? }[1] = 2
or:
ary.select_refs {|x| x.nil? }[1] = 2

Devin
 
L

Logan Capaldo

Daniel said:
How this would work:
Currently, the following would default to "i" if the element doesn't exist.
[4, 6, 9].fetch(4, "i")
This would do the same thing:
[4, 6, 9][4].default("i") or [4, 6, 9].fetch(4).default("i")
I get along fine with:
[4, 6, 9].fetch(4)||'i'

Sure, it has false misses on instances of FalseClass, but I live with
the pain. Besides, what if the above array contained nil?
This would allow things like:
ary = [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # => [4, nil, 6, 2]
May I suggest:
ary.select{|x| x.nil?}[1] = 2
as a better interface.

= isn't overridable, but []= is.

I see more potential in your second thing... Build it as a stand-alone
Ruby library first.

Devin
Here, I'll evenget you started:

% cat view.rb
class ArrayView
class ArrayIndexRef
def initialize( array, index )
@array = array
@index = index
end

def value
@array[@index]
end

def value=(new_value)
@array[@index] = new_value
end
end

def initialize( array )
@array = array
@references = []
end

def [](*args)
if args.length == 1 and args.kind_of? Range or args.length > 1
@references[*args].map { |x| x.value }
else
@references[*args].value
end
end

def []=(index, value)
@references[index].value = value
end

def each
@references.each do |x|
yield x.value
end
end


def add_ref( index )
@references << ArrayIndexRef.new( @array, index )
end
end


class Array
def select_view
r = ArrayView.new( self )
each_with_index do |item, index|
r.add_ref( index ) if yield( item )
end
r
end
end

a = (1..10).to_a

p a
b = a.select_view { |x| (x % 2).zero? }
b[0] = 42
p a

% ruby view.rb
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 42, 3, 4, 5, 6, 7, 8, 9, 10]
 
D

Daniel Finnie

I did something like that for the current Ruby Quiz, but it doesn't do
the []=, which I think is cool. The code is a bit shorter though:
class ArrayValue
instance_methods.each do |m|
undef_method(m) unless m =~ /^_*(method_missing|send|id)_*$/
end

def initialize(origArray, origIndex)
@origArray, @origIndex = origArray, origIndex
end

def set(newObj)
@origArray[@origIndex] = newObj
end

def get
@origArray[@origIndex]
end

def method_missing(method, *args)
get.send(method, *args)
rescue
super
end
end

class Array
def to_av()
ret = []
each_index {|x| ret << ArrayValue.new(self, x) }
ret
end
end

Sample usage:
daniel@daniel-desktop:~$ irb -r 'arrayvalue.rb'

irb(main):001:0> ary = [1, 2, 3, 4, "SomeString", :ASymbol]
=> [1, 2, 3, 4, "SomeString", :ASymbol]

# Set the first String to 42.
irb(main):002:0> ary.to_av.find{|x| x.kind_of?(String)}.set(42)
=> 42

# Changes are reflected in the original array.
irb(main):003:0> ary
=> [1, 2, 3, 4, 42, :ASymbol]

# Set the 3rd Numeric value to 43
irb(main):004:0> ary.to_av.select{|x| x.kind_of?(Numeric)}[2].set(43)
=> 43

# Changes are reflected in the original array.
irb(main):005:0> ary
=> [1, 2, 43, 4, 42, :ASymbol]

Logan said:
Daniel said:
How this would work:
Currently, the following would default to "i" if the element doesn't exist.
[4, 6, 9].fetch(4, "i")
This would do the same thing:
[4, 6, 9][4].default("i") or [4, 6, 9].fetch(4).default("i")
I get along fine with:
[4, 6, 9].fetch(4)||'i'

Sure, it has false misses on instances of FalseClass, but I live with
the pain. Besides, what if the above array contained nil?
This would allow things like:
ary = [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # => [4, nil, 6, 2]
May I suggest:
ary.select{|x| x.nil?}[1] = 2
as a better interface.

= isn't overridable, but []= is.

I see more potential in your second thing... Build it as a stand-alone
Ruby library first.

Devin
Here, I'll evenget you started:

% cat view.rb
class ArrayView
class ArrayIndexRef
def initialize( array, index )
@array = array
@index = index
end

def value
@array[@index]
end

def value=(new_value)
@array[@index] = new_value
end
end

def initialize( array )
@array = array
@references = []
end

def [](*args)
if args.length == 1 and args.kind_of? Range or args.length > 1
@references[*args].map { |x| x.value }
else
@references[*args].value
end
end

def []=(index, value)
@references[index].value = value
end

def each
@references.each do |x|
yield x.value
end
end


def add_ref( index )
@references << ArrayIndexRef.new( @array, index )
end
end


class Array
def select_view
r = ArrayView.new( self )
each_with_index do |item, index|
r.add_ref( index ) if yield( item )
end
r
end
end

a = (1..10).to_a

p a
b = a.select_view { |x| (x % 2).zero? }
b[0] = 42
p a

% ruby view.rb
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 42, 3, 4, 5, 6, 7, 8, 9, 10]
 
D

Daniel Finnie

First, Sorry for the late reply.

Without using additional variables and LOCs, I don't see a way for your
code (even the 1.9 version) to change the nth nil statement as is
possible with the ArrayValue class.

Dan

Martin said:
This would allow things like:
ary = [4, nil, 6, nil]
ary.select{|x| x.nil?}[1].set(2)
ary # => [4, nil, 6, 2]

This will work in 1.8:

class Array
def map_if!(condition)
map! {|i| condition.call(i) ? yield i : i}
end
end

ary.map_if! (lambda {|i| i.nil?) {|x| replacement for x}

1.9 allows blocks to take other blocks as arguments, which will (I
think!) allow something like

class Array
def map_if!(&blk)
_map_if(blk)
end

def _map_if(blk)
map! {|i|
blk.call(i) ? yield i : i
}
end
end

ary.map_if {|x| x.nil?} {|x| do something with x}

Seems more generally useful than setting selected elements by index
(internal versus external iterator)

martin
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top