Ruby: the non-awesome parts

39 views
Skip to first unread message

Andrew Grimm

unread,
Jun 14, 2011, 4:17:42 AM6/14/11
to rails-...@googlegroups.com
During Railscamp, I was thinking of doing a talk "Ruby: The Awesome
Parts". Its aim wasn't to teach how to program in Ruby, but to show
parts of Ruby that are likely to be useful that some people may not
know about. For example, I wish I knew about modules earlier than I
did. I didn't go ahead with it 'cause I wanted a break from talking,
and I didn't get around to it.

However, that led me to wondering "What are the non-awesome parts?"

The main things I thought of were:
* Things that were added to be backwards-compatible with Perl. For
example, most things starting with a dollar sign ($STDERR versus
STDERR)
* Things you can use, but probably shouldn't. For example, class variables (@@).
* Things that are overkill for you to use, but are probably
appropriate for framework writers. Much of this is metaprogramming.
For example, at_exit, define_method, and instance_eval.

Being slow (or not scaling) wouldn't count, because that's a property
of the implementation, not the language. Nor would "Generally used by
opinionated egoists" ("that's a bug?").

Any thoughts?

Andrew

Pat Allan

unread,
Jun 14, 2011, 4:21:51 AM6/14/11
to rails-...@googlegroups.com
What should be used instead of class variables? I agree they're ugly, but in some situations they're the best fit. Would love to see some other options.

--
Pat

> --
> You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group.
> To post to this group, send email to rails-...@googlegroups.com.
> To unsubscribe from this group, send email to rails-oceani...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.
>

Andrew Snow

unread,
Jun 14, 2011, 4:30:06 AM6/14/11
to rails-...@googlegroups.com
On 14/06/11 18:21, Pat Allan wrote:
> What should be used instead of class variables? I agree they're ugly, but in some situations they're the best fit. Would love to see some other options.

Instance variables on the class object ("self").

The differences are:

1. @@variables don't default to nil if you haven't initialised them yet

2. @@variables are inherited to subclasses, so if you initialize one in
the parent then you can see the value in the child. Instance variables
set on the parent Class object local to that object only and are not
inherited to child Class objects.

This behaviour isn't generally what you want so to avoid confusion the
convention is to stick to regular instance @variables on the class object.


- Andrew

Eric Harrison

unread,
Jun 14, 2011, 4:57:01 AM6/14/11
to rails-...@googlegroups.com
Another problem is inherited subclasses will over write the value of
an class variable in the parent class.

I like the instance variables on the class object, hadn't thought of
that solution.

-Eric

Pat Allan

unread,
Jun 14, 2011, 5:59:41 AM6/14/11
to rails-...@googlegroups.com
Right - which means using the eigenclass (which itself feels a little ugly too), yes? Or do @variables within a class method (def self.foo) work fine as well?

--
Pat

Nicholas Jefferson

unread,
Jun 14, 2011, 6:10:55 AM6/14/11
to rails-...@googlegroups.com
> Or do @variables within a class method (def self.foo) work fine as well?

Yes, that accesses the instance variable on the current receiver, which
is the class object. You can also use attr_accessor:

class Foo
class << self
attr_accessor :bar
end
end

Foo.bar = "bar"
p Foo.bar

Thanks,

Nicholas

Pat Allan

unread,
Jun 14, 2011, 6:24:29 AM6/14/11
to rails-...@googlegroups.com
Yup, had used attr_accessor before, but didn't know @variables worked fine within normal class methods without the need to be within the eigenclass. I guess I could have checked that myself with a quick code test :)

Thanks for clarifying Nicholas.

Cheers

--
Pat

Korny Sietsma

unread,
Jun 14, 2011, 8:46:10 AM6/14/11
to rails-...@googlegroups.com
A few broken things in Ruby, or things I meet in other languages that would be nifty in Ruby:

- the broken proc/lambda differences; why are they different?

- throw/catch vs raise/rescue - is it just me, or is it strange and confusing that these are both valid, but for quite different things?  It's probably not a problem if you always do ruby, but if you switch languages it's easy to get these wrong.

- immutable variables, especially persistent data structures.  Simon Harris has written some - see https://github.com/harukizaemon/hamster - but they can't be as efficient as native C-based code, and as they're not a language primitive, they won't get widely used.  Also lazy evaluation and tail-call optimisation, but I guess those are less of a priority, unless you wanted to make a ruby-like fully functional language.

- real threading!

- some other way to do regular expressions.  I keep falling back to the perl-style $1 $2 etc, even though I know they are wrong, because the right way seems counter intuitive and verbose.  (There may be better way to do regexes that I've missed)

- destructuring assignment of hashes (with nesting). You can do this with an array:
  a, b = [1,2]
but in some languages you can do pattern-matching style assignment:
    {:a => a, :b => { :c => [c,d]}} = {:a => 1, :b => { :c => ["foo", "bar"] } }
(which would give you a == 1, c == "foo", d == "bar")
- it would probably need quite a different syntax.  But this sort of thing is great for pulling apart complex nested data structures (like most JSON api responses!)

Note that I have much much longer lists of the things in Ruby that I keep missing when I use other languages :)  (for a start, heredocs, blocks, %Q{} and %w !)

- Korny

--
You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group.
To post to this group, send email to rails-...@googlegroups.com.
To unsubscribe from this group, send email to rails-oceani...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.




--
Kornelis Sietsma  korny at my surname dot com http://korny.info
"Every jumbled pile of person has a thinking part
that wonders what the part that isn't thinking
isn't thinking of"

Dogma

unread,
Jun 14, 2011, 8:35:07 AM6/14/11
to Ruby or Rails Oceania
Are you sure it's a bug? Could be a feature... :)

Gregory McIntyre

unread,
Jun 15, 2011, 4:33:28 AM6/15/11
to rails-...@googlegroups.com
class << self; self; end # explain what self is in 20 words or less

require File.expand_path('foo/bar', File.dirname(__PATH__)) # why is
this not a built-in function?

Lack of Python keyword arguments. Having to use symbol-keyed hashes
and syntax sugar to approximate such a common case and ending up with
things like "opts ||= {}" boilerplate and symbol soup ("as: :person").

x = proc{|a| a*2 }; [1,2,3].map(&x) # explain why you can't call
map(x). Explain block constructor operator & and how it compares to
something like def func(a, b, &block).

def x() end # doesn't evaluate to a function-like object like you
might expect if you come from ECMAScript

Time vs Date vs DateTime vs TZInfo (vs ActiveSupport::TimeZone). Which
do I use and when?

File vs Dir vs FileUtils vs Pathname vs libs like open-uri.

People who increase indent after "private".

Andrew Grimm

unread,
Jun 16, 2011, 5:51:20 AM6/16/11
to rails-...@googlegroups.com

On 15/06/2011, at 6:33 PM, Gregory McIntyre <blue...@gmail.com> wrote:

> class << self; self; end # explain what self is in 20 words or less
>

Your own exclusive top priority module? :)

> require File.expand_path('foo/bar', File.dirname(__PATH__)) # why is
> this not a built-in function?
>

Relative_require ?

> Lack of Python keyword arguments. Having to use symbol-keyed hashes
> and syntax sugar to approximate such a common case and ending up with
> things like "opts ||= {}" boilerplate and symbol soup ("as: :person").
>

you can do opts = {} in the method definition.

> x = proc{|a| a*2 }; [1,2,3].map(&x) # explain why you can't call
> map(x).

I'd be curious about how you would handle Array.new(10, x) where x could be either a proc or an object? (not to mention that procs are objects!)


> Explain block constructor operator & and how it compares to
> something like def func(a, b, &block).
>

Isn't that a good symmetry, just like the splat operator?


> def x() end # doesn't evaluate to a function-like object like you
> might expect if you come from ECMAScript
>

It's possible to get a method. The only downside is that you can only bind it to the same class. It's sometimes used to make the original version of a monkeypatched method invisible to everything but the new version.

Python would allow you to do foo as a reference to the method and foo() as calling the method. That's kind of nice, but means more brackets than Ruby.

> Time vs Date vs DateTime vs TZInfo (vs ActiveSupport::TimeZone). Which
> do I use and when?
>

I only use time when doing ghetto profiling. Rails developers may be better able to answer this one.

> File vs Dir vs FileUtils vs Pathname vs libs like open-uri.
>

I don't think I've used the middle two.

> People who increase indent after "private".
>

People use private? Actually, private is the kind of thing you read about in the manuals but don't actually use, so thanks for mentioning that!

Mark Wotton

unread,
Jun 16, 2011, 6:20:13 AM6/16/11
to rails-...@googlegroups.com
On Thu, Jun 16, 2011 at 7:51 PM, Andrew Grimm <andrew....@gmail.com> wrote:


On 15/06/2011, at 6:33 PM, Gregory McIntyre <blue...@gmail.com> wrote:

> x = proc{|a| a*2 }; [1,2,3].map(&x) # explain why you can't call
> map(x).

I'd be curious about how you would handle Array.new(10, x) where x could be either a proc or an object? (not to mention that procs are objects!)

This is a bad argument. You should be able to treat functions just as normal values - the weird soup of procs, lambdas and "proper" functions that Ruby has is a bit of a hack.
Languages that treat functions as unproblematic values collapse all this complexity down to almost nothing.
 
> Time vs Date vs DateTime vs TZInfo (vs ActiveSupport::TimeZone). Which
> do I use and when?
>

I only use time when doing ghetto profiling. Rails developers may be better able to answer this one.

it's a hack:) but this is about organisation of the libraries, and subject to historical accidents. It could be changed.
 
> People who increase indent after "private".
>

People use private? Actually, private is the kind of thing you read about in the manuals but don't actually use, so thanks for mentioning that!

protected is more common, but a lot of people (ooh, weasel words!) claim that even protected is a code smell - that it's a sign that you should break that thing out into a separate object.

mark 

--
A UNIX signature isn't a return address, it's the ASCII equivalent of a
black velvet clown painting. It's a rectangle of carets surrounding a
quote from a literary giant of weeniedom like Heinlein or Dr. Who.
        -- Chris Maeda

Gregory McIntyre

unread,
Jun 16, 2011, 7:52:16 AM6/16/11
to rails-...@googlegroups.com
On 16 June 2011 19:51, Andrew Grimm <andrew....@gmail.com> wrote:
>> Lack of Python keyword arguments. Having to use symbol-keyed hashes
>> and syntax sugar to approximate such a common case and ending up with
>> things like "opts ||= {}" boilerplate and symbol soup ("as: :person").
>
> you can do opts = {} in the method definition.

Sure. But I prefer

def f(opts = nil)
opts ||= {}
...
end

over

def f(opts = {})
...
end

because I find it more robust. nil means the absence of a value and it
fits better with the many Ruby stdlib methods that return nil when
there is no value found. Like anything with 'try'
(http://api.rubyonrails.org/classes/Object.html#method-i-try) in it,
and... um...

f(configuration_file[:options_for_function_f])

Also, if you ever find yourself overriding this method in a subclass,
it's easier to remember that the only optional value you ever need to
specify is nil.


>> Explain block constructor operator & and how it compares to
>> something like def func(a, b, &block).
>
> Isn't that a good symmetry, just like the splat operator?
>
>> def x() end # doesn't evaluate to a function-like object like you
>> might expect if you come from ECMAScript
>
> It's possible to get a method. The only downside is that you can only bind it to the same class. It's sometimes used to make the original version of a monkeypatched method invisible to everything but the new version.

Sure. They're easy to understand *once you're used to them*. I just
think of them as learning hurdles for new Rubyists. They're all facets
of the same problem - that in Ruby, procs, methods and blocks are all
slightly different.


> Python would allow you to do foo as a reference to the method and foo() as calling the method. That's kind of nice, but means more brackets than Ruby.

I do love the Uniform Access Principle. Go Bertrand.


>> Time vs Date vs DateTime vs TZInfo (vs ActiveSupport::TimeZone). Which
>> do I use and when?
>
> I only use time when doing ghetto profiling. Rails developers may be better able to answer this one.

Time is a timestamp and can represent a moment in time no matter where
it occurred in the world, agnostic of timezone. Think UNIX timestamps.
Date is a thing on a calendar that might mean a different moment in
time to different people depending on their timezone. Think of Date
for things like recording your birthday. DateTime subclasses Date and
adds hour, minute, second and timezone information to a Date so it
becomes a lot like Time, but that makes me unsure when to use DateTime
and when to use Time, to be honest.

Time has timezone capabilities built in. TZInfo has a different set of
timezone capabilities and operates with Time objects, but seems to
ignore their built-in timezone settings. I'm still not sure why.


>> File vs Dir vs FileUtils vs Pathname vs libs like open-uri.
>
> I don't think I've used the middle two.

FileUtils has functions like you'd call on the command line, to move
and copy files around. Like shutil in Python. But, File and Dir also
have some command-line-like functionality like Dir.glob. Pathname
tries to unify a lot of this stuff in a more O-O manner but it is a
wrapper around File and Dir and this is apparent when you try to read
its documentation, so it doesn't feel like it truly encapsulates its
concerns. Also you have to require 'pathname' which isn't so bad
except when you have to do it before you start using Pathname to
require relative files.


On 16 June 2011 20:20, Mark Wotton <mwo...@gmail.com> wrote:
> protected is more common, but a lot of people (ooh, weasel words!) claim
> that even protected is a code smell - that it's a sign that you should break
> that thing out into a separate object.
> mark

Got any links to "private considered harmful" type articles?

I always prefer private over protected because private is simple and
does not convolute a class's interface. To me, protected implies an
overengineering-like reliance on the inheritance mechanism which IMHO
should be used sparingly, with shallow hierarchies. I think of
protected as a code smell.

--
Gregory McIntyre

Korny Sietsma

unread,
Jun 16, 2011, 8:41:19 AM6/16/11
to rails-...@googlegroups.com
+1 on private - it's more documentation than anything else - "these bits are implementation details, don't call them externally".  I don't really care about how the "private" keyword actually works.  "Protected" on the other hand means "I'm doing something bizarre" :)

- Korny

--
You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group.
To post to this group, send email to rails-...@googlegroups.com.
To unsubscribe from this group, send email to rails-oceani...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.

Andrew Grimm

unread,
Jun 16, 2011, 8:58:05 AM6/16/11
to rails-...@googlegroups.com
On Thu, Jun 16, 2011 at 8:20 PM, Mark Wotton <mwo...@gmail.com> wrote:
>
>
> On Thu, Jun 16, 2011 at 7:51 PM, Andrew Grimm <andrew....@gmail.com>
> wrote:
>>
>>
>> On 15/06/2011, at 6:33 PM, Gregory McIntyre <blue...@gmail.com> wrote:
>>
>> > x = proc{|a| a*2 }; [1,2,3].map(&x) # explain why you can't call
>> > map(x).
>>
>> I'd be curious about how you would handle Array.new(10, x) where x could
>> be either a proc or an object? (not to mention that procs are objects!)
>
> This is a bad argument. You should be able to treat functions just as normal
> values - the weird soup of procs, lambdas and "proper" functions that Ruby
> has is a bit of a hack.
> Languages that treat functions as unproblematic values collapse all this
> complexity down to almost nothing.
>

I'm not understanding what you're saying.

Given

x = proc {|a| a * 2}
[1,2,3].fancy_map(x)

you'd have to have

module Enumerable
def fancy_map(x)
result = []
each do |element|
result << x.call(element)
end
result
end
end

that in of itself is fairly innocuous. but what about Array.new ?

There's three forms of Array.new currently:

Array.new(size=0, obj=nil)
Array.new(array)
Array.new(size) {|index| block }

For the purposes of this discussion, we don't have to worry about
Array.new() or Array.new(existing_array). Under the current syntax, we
can do the following:

x = proc {|a| a * 2}
Array.new(3, "close to deadline") # Works, gives ["close to deadline",
"close to deadline", "close to deadline"]
Array.new(3, &x) # Works, gives [0, 2, 4]
Array.new(3, x) # Less common but works, gives [a_proc_proc_here,
a_proc_proc_there, here_a_proc_there_a_proc_everywhere_a_proc_proc]

But what'd happen if you banned the lambda? You'd either not be able
to do the equivalent of Array.new(3, &x), or you wouldn't be able to
do the equivalent of Array.new(3, x)

I'm not sure what approach Haskell takes with this, however. Perhaps
it manages to handle this issue easily.

Andrew

Andrew Snow

unread,
Jun 16, 2011, 9:18:09 AM6/16/11
to rails-...@googlegroups.com

I like to think of:

def foo(a, b=nil) ... end

as a shortcut or macro for:

define_method :foo, ->(a, b=nil) { ... }

Mark Wotton

unread,
Jun 16, 2011, 9:36:40 AM6/16/11
to rails-...@googlegroups.com
You're exploiting the fact that you've effectively got two argument lists. The first is the normal list of arguments - the second is an optional block. 

So, you get a bit of a syntactic shortcut when the block is nil - you just don't specify it. Similarly, you can leave off the default arguments and still give it a block, because your argument list has been split into two chunks.

All of this comes at the cost of complicating your basic model of function invocation, which you do all the time. I posit that it's not even close to worth it.

In haskell, you'd just pass in 'id' for the function argument if you didn't need the block. Easy, if a bit of a code smell.

mark
waiting for rob postill to weigh in

Nicholas Jefferson

unread,
Jun 16, 2011, 11:18:10 PM6/16/11
to rails-...@googlegroups.com
> Lack of Python keyword arguments. Having to use symbol-keyed hashes
> and syntax sugar to approximate such a common case and ending up with
> things like "opts ||= {}" boilerplate and symbol soup ("as: :person").

> [...]

> f(configuration_file[:options_for_function_f])

Ok, so how does that work with Python keyword arguments? Say..

configuration_file = {}

def f(bar = None, baz = None):
return [bar, baz]

# fails with KeyError:
options = configuration_file["options_for_function_f"]

# try defaulting to None..
options = configuration_file.get("options_for_function_f", None)

# fails with TypeError:
print f(**options)

Thanks,

Nicholas

Gregory McIntyre

unread,
Jun 17, 2011, 1:20:59 AM6/17/11
to rails-...@googlegroups.com
Oh. I wouldn't even attempt doing that in Python. Hahaha. Python's
default values are broken as far as I'm concerned. [1] Many standard
lib Python functions raise errors instead of returning None anyway,
like dict['key']. Its falsey value situation is more complicated. So
given all that, trying to do this kind of "nil is a valid value in
your train wreck" thing in Python is fraught with difficulty.

But I'm still envious of its neatly built-in keyword arguments. I
think of all the procedures where there is a natural ordering to
arguments versus all the procedures where the order of arguments is
arbitrary and find it annoying that I have to remember what order to
pass things when it really doesn't matter most of the time.

[1]

def f(bar = {}):
print bar
bar['hi'] = 'there'

f(); f()

prints:

{}
{'hi': 'there'}

i.e. Python only ever initialises the default value for bar once. You
should only ever use immutable objects as default values. Which, if
you ask me, is a nasty gotcha.

--
Gregory McIntyre

SengMing Tan

unread,
Jun 24, 2011, 2:41:32 AM6/24/11
to Ruby or Rails Oceania
Since no one's mentioned it I could be the only one that's bothered by
how modules/mixins are handled:

1. I can't tell the difference between 'include' and 'extend' just
from the names
2. the 'self.included' trick of mixing class and instance methods to a
module (see last example of http://railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/)
feels like a hack.

On Jun 14, 6:17 pm, Andrew Grimm <andrew.j.gr...@gmail.com> wrote:

Ryan Bigg

unread,
Jun 24, 2011, 3:58:36 AM6/24/11
to rails-...@googlegroups.com
Include == you are INCLUDING the methods of the module into the class.

Extend == you are EXTENDING the class with the methods from the module.

Once I understood that, it was all clear.

self.included and self.inherited are both fun. What would you do instead?

Clifford Heath

unread,
Jun 24, 2011, 4:15:47 AM6/24/11
to rails-...@googlegroups.com
On 24/06/2011, at 5:58 PM, Ryan Bigg wrote:
> Extend == you are EXTENDING the class with the methods from the
> module.

The first time I used extend, it was on instances, not on classes.
I was extending those instances... that doesn't make sense for
include, which only works on classes.

Clifford Heath.

Andrew Grimm

unread,
Jun 25, 2011, 12:52:37 AM6/25/11
to rails-...@googlegroups.com
+1 There's nothing classy about being extended!

Andrew

Reply all
Reply to author
Forward
0 new messages