Open jordaaash opened 10 years ago
Reading the description of the repository, I guess you are taking it too serious - I think it's rather "check out what cool things we can do" instead of "this will be official and the only one implementation of swift features" kind of repository.
I can't do that because in swift-ruby, that construct is object.foo?? which is not valid syntax and will presumably error
It's actually not an error, but the beginning ternary operator.
Examples of methods with question mark:
[9] pry(main)> class Foo; def bar; puts "bar"; end; def bar?; puts "bar?"; end; end;
[10] pry(main)> f = Foo.new
=> #<Foo:0x000000019bd6a0>
[11] pry(main)> f.bar
bar
=> nil
[12] pry(main)> f.bar?
bar?
=> nil
[13] pry(main)> f.bar??
[13] pry(main)*
[14] pry(main)> f.bar?? 0 : 1
bar?
=> 1
Anyway, ?
is prettier than try(...)
, but one can write similar code like in this repo, but with prefix character (not necessarily ?
), rather than suffix.
?
may be prettier than try
, but that doesn't make it more useful. I'd still argue that it's less explicit, less descriptive, less flexible, and more ambiguous. The point that I was making is that many of Swift's features are built around Optionals, which are really a specific way of introducing optional types and implicit null checks into a statically typed language and getting compile time safety instead of runtime null references.
But Ruby is not a statically typed language, nor is it a compiled language. In short, Ruby is not Swift. What makes it a cool feature for Swift is that it is cool for Swift. It doesn't make much sense to port language features, cool or otherwise, from one language to another where they don't make much sense in the target language.
Great thread and great points.
I only skimmed the comments but I have a feeling I'm going to agree with most of your points.
But...it is an experiment, and a playground.
With a slim hope that if something cool comes out it will be useful - something like activesupport - adding a lot of c
...continued
Cool features that make using ruby that much better.
I read that Rust's creator thought Swift is very close to Rust, but admitted that Rust borrowed a lot of the best ideas from other languages such as Haskell, Lua, SmallTalk, Ruby, Python.
This is what inspired this.
I was thinking "we can steal good ideas from other languages...we don't have to wait for the Ruby Core team to do it".
So I set off to see what was possible.
Maybe I didn't chose the best starting point :)
So - I'm off to read the Swift language guide to see what else looks good, and invite you to do the same, to see how far we can bend the ruby language itself.
(I think pretty far, but we shall see!)
In fact...maybe this experiment should be broader and just be a "ruby+otherlaguages" project. For example Objective C's feature (likely swift too?) that sending a message to nil returns nil and doesn't raise an error is also nice. Can that be implemented in ruby?
class NilClass
def method_missing (*)
nil
end
end
As with most attempts to silently swallow errors, if you think this is a good idea, you're gonna have a bad time.
Correction: If you think this is a good idea, you and anyone whose Gemfile.lock ends up including your library, intentionally or otherwise, are all gonna have a bad time.
First of all using try
is not good, cause you are asking your object about its state which is the opposite of tell don't ask rule. Additionally this feature is already there and can be easily implemented
class Foo
include ActiveModel::AttributeMethods
attribute_method_suffix '?'
def attribute?(attribute)
send(attribute).present?
end
end
I disagree that try
is bad, as "tell, don't ask" is not only overly broad IMO, but doesn't apply to most metaprogramming like this, especially with method_missing
which relies on respond_to?
and therefore must necessarily ask the object about its "state".
Additionally, attribute_method_suffix
, while elegant, is not suitable for this purpose.
ActiveModel::AttributeMethods has many requirements, one of which is:
Define an attributes method which returns a hash with each attribute name in your model as hash key and the attribute value as hash value
This is incompatible with the use case this feature purports to satisfy, which is not to call methods and return a boolean as attribute?
does, but to call them only if they are defined and return their value, or return nil
if they are not defined (i.e., what try
does).
Further, attribute?
relies on Object#present?
, another ActiveSupport method, as well as ActiveModel. I maintain that this entire use case and the related cases I described are satisfied (in a strictly better fashion) by try
. There really is no need to reinvent that wheel, nor a need to port narrowly construed Swift features to Ruby.
You don't have to use Object#present?
it was just an example. Minimal setup is
class Foo
include ActiveModel::AttributeMethods
attribute_method_suffix '?'
define_attribute_methods :name, :lastname
attr_accessor :name, :lastname
def initialize(opts = {})
@name = opts[:name]
@lastname = opts[:lastname]
end
private
def attribute?(attribute)
send(attribute)
end
end
I went again through your example on main page and my bad cause I misunderstood that feature. It's more like coffeescript function?()
Thanks everyone for this interesting conversation.
As you all probably know, Matz announced that this feature is coming to Ruby, using the "lonely operator": &.
*
So I wasnt THAT crazy, but I admit I chose a bad syntax.
The lonely operator works like this:
employee&.department&.address&.city
* According to Matz, he's calling it the lonely operator because: > Its a guy, sitting on the floor, alone, looking at a dot. Look at him. He's lonely.
The benefits of ActiveSupport's use of
#try
with a symbol for handling this feature are many.First and foremost, there is already a very well-established Ruby convention for question mark methods, which are methods that (usually) take no arguments, return a boolean value, and are usually idempotent -- they don't change anything about the object's state when called, they only report it. This is in fact so common it's baked into basic objects for methods like
#nil?
,#empty?
, and many, many others, as well as countless libraries that already use the convention.By ignoring this convention, swift-ruby creates ambiguity for a questionable benefit that is already handled quite elegantly by
#try
, and it does so in a way that prevents its use in obvious special cases.When I call
#active?
on my object, am I perhaps checking the state ofactive
(idempotent) or am I actually callingactive
on the object, perhaps changing the state (not idempotent) of the object?Let's assume (as swift-ruby does) that it's the latter. Even if I choose to make this assumption for my object, since I can't predict what classes may subclass said object, how can I know someone won't implement an
#active?
method for checking it's "active" state later, thereby breaking any code (including external code) that used swift-ruby'sactive?
to call it?Preventing this kind of ambiguity is what conventions are for. Ignoring the fact that it's pretty reckless to override
#method_missing
onObject
in code intended for a library or language feature, and the performance issues that arise from that choice, it's not only unclear what you're doing when you call#foo?
, but it can't even be relied upon to work in all cases.To illustrate, let's further assume that I actually do have another declared method on my object,
#foo?
If I wanted to call this object with swift-ruby if it exists and ignore it otherwise (like I would withobject.try(:foo?)
, I can't do that because in swift-ruby, that construct isobject.foo??
which is not valid syntax and will presumably error.When I see code like
object.try(:foo?)
andactive?
, it's quite apparent from reading what the developer intended. If you intend to port Swift features to Ruby, features that are basically just syntactical sugar, I think it should be done with more regard for the nature of the Ruby language and its conventions.Ruby already has elegant solutions to problems that Swift's Optionals purport to solve, such as default values when setting a variable (using
||=
for example), and especially the#try
feature in question. This feature being proposed creates obvious new problems without having any clear benefit. I don't doubt that Swift will be much more pleasant to code in that Objective C, but that doesn't mean that Ruby has imminent need of borrowing its language constructs.