In some language such as Haskell, it is possible to use any function taking two arguments as an infix operator.
I find this notation interesting and would like to achieve the same in ruby.
Given a imaginary method or_if_familiar
I'd like to be able to write something like "omg" or_if_familiar "oh!" instead of or_if_familiar("omg", "oh!")
How one would create such a notation in ruby (without modifying ruby itself)?
A bit late to the party but I've been toying around with it and you can use operator overloading to create Infix operators just like in python (but with a bit more work), the syntax becomes a |op| b, here's how:
First a quick and dirty copy-paste to play around with Infix:
class Infix def initialize*a,&b;raise'arguments size mismatch'if a.length<0||a.length>3;raise'both method and b passed'if a.length!=0&&b;raise'no arguments passed'if a.length==0&&!b;#m=a.length>0? a[0].class==Symbol ? method(a[0]):a[0]:b;if a.length==3;#c=a[1];#s=a[2]end end;def|o;if#c;o.class==Infix ? self:#m.(#s,o)else;raise'missing first operand'end end;def coerce o;[Infix.new(#m,true,o),self]end;def v o;Infix.new(#m,true,o)end end;[NilClass,FalseClass,TrueClass,Object,Array].each{|c|c.prepend Module.new{def|o;o.class==Infix ? o.v(self):super end}};def Infix*a,&b;Infix.new *a,&b end
#
Ok
Step 1: create the Infix class
class Infix
def initialize *args, &block
raise 'error: arguments size mismatch' if args.length < 0 or args.length > 3
raise 'error: both method and block passed' if args.length != 0 and block
raise 'error: no arguments passed' if args.length == 0 and not block
#method = args.length > 0 ? args[0].class == Symbol ? method(args[0]) : args[0] : block
if args.length == 3; #coerced = args[1]; #stored_operand = args[2] end
end
def | other
if #coerced
other.class == Infix ? self : #method.call(#stored_operand, other)
else
raise 'error: missing first operand'
end
end
def coerce other
[Infix.new(#method, true, other), self]
end
def convert other
Infix.new(#method, true, other)
end
end
Step 2: fix all the classes that don't have a | method and the three special cases (true, false, and nil) (note: you can add any class in here and it will probably work fine)
[ NilClass, FalseClass, TrueClass,
Float, Symbol, String, Rational,
Complex, Hash, Array, Range, Regexp
].each {|c| c.prepend Module.new {
def | other
other.class == Infix ? other.convert(self) : super
end}}
Step 3: define your operators in one of 5 ways
# Lambda
pow = Infix.new -> (x, y) {x ** y}
# Block
mod = Infix.new {|x, y| x % y}
# Proc
avg = Infix.new Proc.new {|x, y| (x + y) / 2.0}
# Defining a method on the spot (the method stays)
pick = Infix.new def pick_method x, y
[x, y][rand 2]
end
# Based on an existing method
def diff_method x, y
(x - y).abs
end
diff = Infix.new :diff_method
Step 4: use them (spacing doesn't matter):
2 |pow| 3 # => 8
9|mod|4 # => 1
3| avg |6 # => 4.5
0 | pick | 1 # => 0 or 1 (randomly chosen)
You can even kinda sorta curry:
(This only works with the first operand)
diff_from_3 = 3 |diff
diff_from_3| 2 # => 1
diff_from_3| 4 # => 1
diff_from_3| -3 # => 6
As a bonus, this little method allows you to define Infixes (or any object really) without using .new:
def Infix *args, &block
Infix.new *args, &block
end
pow = Infix -> (x, y) {x ** y} # and so on
All that's left to do is wrap it up in a module
Hope this helped
P.S. You can muck about with the operators to have something like a <<op>> b, a -op- b, a >op> b and a <op<b for directionality, a **op** b for precedence and any other combination you want but beware when using true, false and nil as the first operand with logical operators (|, &&, not, etc.) as they tend to return before the infix operator is called.
For example: false |equivalent_of_or| 5 # => true if you don't correct.
FINALLY, run this to check a bunch of cases of all the builtin classes as both the first and second operand:
# pp prints both inputs
pp = Infix -> (x, y) {"x: #{x}\ny: #{y}\n\n"}
[ true, false, nil, 0, 3, -5, 1.5, -3.7, :e, :'3%4s', 'to',
/no/, /(?: [^A-g7-9]\s)(\w{2,3})*?/,
Rational(3), Rational(-9.5), Complex(1), Complex(0.2, -4.6),
{}, {e: 4, :u => 'h', 12 => [2, 3]},
[], [5, 't', :o, 2.2, -Rational(3)], (1..2), (7...9)
].each {|i| puts i.class; puts i |pp| i}
In Ruby, whether the operator is prefix or infix is fixed by the parser. Operator precedence is also fixed. There is no way, short of modifying the parser, of changing these things.
But you can implement the built-in operators for your objects
Although you may not change the fix-ness or precedence of a built-in operator, you may implement operators for your objects by defining methods. That is because Ruby translates operators into method calls. For example, this expression:
a + b
is translated into:
a.+(b)
Therefore, you may implement the + operator for an arbitrary object by defining the + method:
def +(rhs)
...
end
The prefix operator - causes a call to method #-, so to implement prefix - you do this:
def #-
..
end
You may also use methods
You may implement your own infix operators as plain methods. This will require a slightly different syntax than what you want. You want:
"omg" or_if_familiar "oh!"
Which you cannot have. What you can have is:
"omg".or_if_familiar "oh!"
This works because, in Ruby, the parentheses on method arguments may often be omitted. The above is equivalent to:
"omg".or_if_familiar("oh!")
In this example, we would implement this by monkey-patching the String class:
class String
def or_ir_familiar(rhs)
...
end
end
Ruby does not have infix method syntax, except for a fixed and predefined set of operators. And Ruby does not allow user code to change the language syntax. Ergo, what you want is not possible.
Based on Wayne Conrad's answer, I can write the following code that would work for any method defined in ruby top-level:
class Object
def method_missing(method, *args)
return super if args.size != 1
# only work if "method" method is defined in ruby top level
self.send(method, self, *args)
end
end
which allows to write
def much_greater_than(a,b)
a >= b * 10
end
"A very long sentence that say nothing really but should be long enough".much_greater_than "blah"
# or
42.much_greater_than 2
Thanks Wayne!
Interesting reference on the same subject:
Defining a new logical operator in Ruby
Related
I have the following two methods, which I believe should have the same behaviour disregarding their names:
def a=(*params)
params
end
def b(*params)
params
end
But when in fact I use them:
a=(1) # => 1
b(1) # => [1]
(a=1) == b(1) # => false
while interestingly:
(a=1,2) == b(1,2) # => true
Why isn't their behaviour the same?
Edit: forgot to wrap the above in a class / call with self. which accidentally produces the same behaviour but for a different reason. It has been pointed out in the answers.
It has nothing to do with splat. It's the assignment operator. In ruby, the assignment operator returns the value assigned. The return value from the method is ignored.
So a=1 return 1, not [1].
But, as mentioned by #mudasobwa, you're not even calling the method here. But if you were, that's what would happen (ignoring the return value).
class Foo
def a=(*params)
params
end
end
f = Foo.new
f.a = 1 # => 1
f.a = 1,2 # => [1, 2]
To not ignore the return value, call that setter without using assignment operator.
f.send 'a=', 1 # => [1]
The thing is that
a = 1
sets the local variable and does not call your method at all. Try with
def a=(*param)
puts "I AM HERE"
end
var= methods require an explicit receiver. To call your method, call it with an explicit receiver:
self.a = 1
It still won’t return anything but 1, because assignment methods return the value (the same way as initialize called through MyClass.new returns an instance, no matter what.) But you might check that splat works with:
def a=(*param)
puts param.inspect
end
self.a = 1
# [1]
#⇒ 1
I am currently using the following code in a ruby program to evaluate variable length arguments that are passed to a method. The program is running however I'm wondering if there is a short hand way to write this.
Should have been more specific in my original description, trying to rewrite the Inject method for the Array class (hence the witty name...)
Therefore it needs to be able to accept a maximum of two args, and a minimum 0 if a block is given.
array.inject(:+)
array.inject{ |output, num| output + num }
array.inject(arg, :+)
array.inject(arg) { |output, num| output + num }
The most difficult case/s to handle are the first and forth where the 1 arg can be either a Fixnum or a Symbol. As mentioned, the code works, just looking for ways to tidy it up.
class Array
def enjict(*args)
if args.length == 2 && args[0].is_a?(Fixnum) && args[1].is_a?(Symbol)
start, symbol = args
elsif args.length == 1
raise ArgumentError unless args.first.is_a?(Symbol) || args.first.is_a?(Fixnum)
symbol = args.first if args.first.is_a?(Symbol)
start = args.first if args.first.is_a?(Fixnum)
else
raise ArgumentError unless block_given?
end
copiedArray = dup
start = copiedArray.shift unless start
if block_given?
copiedArray.each { |num| start = yield(start, num) }
else
copiedArray.each { |num| start = start.send(symbol, num) }
end
start
end
end
The sad truth is: it's messy, and there's nothing you can do about it. Almost all Ruby implementations implement Enumerable#inject with privileged access to the interpreter internals, including introspection of the arguments. MRI, YARV, MRuby implement it in C, MacRuby and RubyMotion in Objective-C, XRuby and JRuby in Java, Ruby.NET and IronRuby in C#, Topaz in RPython, Cardinal in PIR, and so on.
This is something that is simply not available to Ruby code.
Only Rubinius implements it in Ruby.
You can use a similar trick by (ab)using the fact that the default argument expression for an optional parameter can be any arbitrarily complex Ruby expression and that local variables of those expressions become local variables of the method. This is a common trick for figuring out whether an argument was passed or not:
def inject(initial=(no_initial = true; nil), sym=(no_sym = true; nil))
sym, initial = initial, nil if !block_given && no_sym
# and so on …
end
Judging from the conditions, how about refactoring your method arguments to:
def enjict(start, symbol, *options, &block)
e = proc{ raise ArgumentError if options.length > 0 && !block_given? }
e.call
if start.is_a?(Fixnum) && symbol.is_a?(Symbol)
# do something you want
else
e.call
end
end
In this article, it uses the following method.
h = {}
def h.[]=(k, v)
puts "Setting hash key #{k} with #{v.inspect}"
super
end
# 1. The standard ||= approach
h[:x] ||= 10
h[:x] ||= 20
...
I understand that this is a setter like this for =( ) part.
def noise=(noise)
#noise = noise
end
Q1. But I am not sure what .[] part is doing.
Q2. Can you use [] or other non-alphabets in a Ruby method name?
Q1. But I am not sure what .[] part is doing.
Almost everything is an object in Ruby, and we can define a method on anything that's an object. So []= is a method defined on the Hash class (an object) in Ruby, just like + and - are methods on numbers (also objects):
> 4 + 5
# => 9
> 4.+(5)
# => 9
> 10.-(3)
# => 7
Likewise, we can call the .[] or .[]= methods for objects that define it, like an Array:
> arr = ['hi', 'there', 'welcome', 'to', 'StackOverflow']
> arr.[](3)
# => "to"
or like your Hash:
> hash = {:name => 'Bob', :age => 27}
> hash[:name]
# => "Bob"
> hash[:name] = 'Steve'
# => "Steve"
Again, Ruby lets us put methods on anything that's an object, and (almost) everything is an object in Ruby. So we can define that .[] method on any class we like:
> class Foo
> def [](arg)
> arg
> end
> end
> Foo.new[456]
> # => 456
Since instances of objects are also objects, we can define that method to be only on specific instances:
> h = {} # a Hash instance
> def h.[](arg) # we're giving this instance a new method
> "received #{arg}"
> end
> h[123]
# => "received 123"
Other instances of the same class won't get that implementation:
> {:foo => :bar}[123] # other Hash instances don't have this method,
# so they're using the default Hash#[] method
# => nil
.[] is something of a special case in one respect, because we let you skip the parentheses and put the argument right inside the brackets:
> arr[3] == arr.[](3)
# => true
Q2. Can you use [] or other non-alphabets in a Ruby method name?
No, you can't use [] in the name of an arbitrary Ruby method. This is an operator (like + or - in the previous example).
You can only overload specific operators, namely (listed in order of precedence):
!, ~, + (unary)
**
- (unary)
*, /, %
+, - (binary)
<<, >>
&
|, ^
<, <=, =>, >
==, ===, !=, =~, !~, <=>
Otherwise, a Ruby method can contain any mixture of alphanumeric Unicode characters and underscores. (I'm using "alphanumeric" loosely here -- Ruby is very liberal in what it allows, and generally speaking any character that isn't otherwise reserved by the language to tokenize things will work.) It may optionally end with a single ! or ?, and it may not start with a number.
So these are valid method names:
present?
valid_user?
replace!
replace
更换 (but you probably should make your method names using the A-Z Latin alphabet so developers don't hate you)
❨╯°□°❩╯︵┻━┻
┬─┬ノ❨º_ºノ❩
replace_all
REPLACE_1
REPLACE_ALL
Note that while the last two are valid method names, by convention Rubyists usually reserve ALL-CAPS identifiers for constants, not methods.
In Ruby, [] and []= are just operators and they can be overloaded.
Here []= is being defined on the singleton class for h, in other words, it is being defined to work a certain way just for h.
So when you say h[something] = somethingelse the new method gets called.
You are right that this use is quite similar to the general use of setters.
To answer the second question, you can overload the operators shown in this table, but you can't just go ahead and create operators like <-*-> as you can in some languages.
There's a method that's usually called with named arguments and it looks like this
def foo(x = nil, y = nil)
fail ArgumentError, "x must be present" unless x
fail ArgumentError, "y must be present" unless y
# do stuff with x and y
end
I want to rewrite as something like
def foo(x = nil, y = nil)
required_arguments :x, :y
# do stuff with x and y
end
or
class Foo
required_arguments :bar, :x, :y
def bar(x = nil, y = nil)
end
end
I've tried to implement second approach with alias_method_chain but the problem is that __method__ is evaluated in the context of my utility module so I can't access the parameters of the method I need to check. Any ideas?
If you use ruby 2.0 you can use keyword arguments:
def foo(x: (fail ArgumentError), y: (fail ArgumentError))
# do stuff with x and y
end
And in ruby 2.1 you have proper required arguments:
def foo(x:, y:)
# do stuff with x and y
end
This way you do actually have named parameters (you called them named parameters in your question, but that's a bit confusing imho), so you have to call the method like this:
foo(x: 1, y: 2)
The value of such run-time assertions is limited, but they do have their use. I have incorporated them in YSupport. Type gem install y_support in your command line, and use it as follows
require 'y_support/typing'
def foo x=nil, y=nil
x.aT; y.aT
# do stuff with x and y
end
Mnemonic: In aT, a means "assert" and T means TypeError – raise TypeError if the assertion fails. If #aT method is called without parameters, it simply enforces that the receiver must be truey. If a block is supplied, any assertion can be written. For example, the following call enforces that the receiver be divisible by 3:
6.aT { |n| n % 3 == 0 } #=> 6
7.aT { |n| n % 3 == 0 } #=> TypeError: 7:fixnum fails its check!
In case of checking the method arguments, ArgumentError is appropriate when there is wrong number of arguments and similar problems. When the arguments are of wrong type, I prefer to raise TypeError. Error message of #aT method can be customized with two string arguments. The first one describes what the receiver, the second one describes the block assertion. For example:
7.aT "number of apples", "be divisible by three" do |n| n % 3 == 0 end
#=> TypeError: Number of apples fails to be divisible by three!
The #aT method, if it passes, returns back its receiver, so the assertions can be chained:
81.aT( "no. of apples", "divisible by 3 ) { |n|
n % 3 == 0
}.aT( "no. of apples", "be a square" ) { |n|
root = n ** 0.5; root == root.floor
} ** 0.5 #=> 9.0
Other, more specialized runtime assertions are available in YSupport, such as:
[ 1, 2, 3 ].aT_kind_of Enumerable #=> [ 1, 2, 3 ]
:foobar.aT_respond_to :each
#=> TypeError: Foobar:symbol does not respond to method 'each'!
:foobar.aT_respond_to :each, "object returned from the black box"
#=> TypeError: Object returned from the black box does not respond to method 'each'!
7.aT_equal 8
#=> TypeError: 7:fixnum must be equal to the prescribed value (8:fixnum)!
You can look up more of those methods by yourself in YSupport, and if you miss something, you are welcome to contribute it.
As a post scriptum to this post, if you are accustomed to ActiveSupport's #present? method, the runtime assertion for it in YSupport is:
[].aT_present "supplied array"
#=> TypeError: Supplied array not present!
this sounds weird doesn't it?
class Dummy
def foo=(value); end
end
Dummy.new.foo = 1 { |x| x } # => syntax error
Dummy.new.foo=(1) { |x| x } # => syntax error
i tried every permutation of blanks, parenthesis, commas; no luck. i'm puzzled. i never suspected methods ending with '=' were special. is it a bug? is it intended? if intended, why? is it documented? where? please share insight.
thanks
ps. ruby is 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.1]
The syntax sugar for methods ending in = does make it special. You can still do things like pass multiple arguments to that method, or pass a block, but not in any pretty or convenient manner:
class Foo
def bar=(a,b=nil)
p [a,b]
if block_given?
yield "hi"
else
puts "No block"
end
end
end
f = Foo.new
f.bar = 42
#=> [42, nil]
#=> No block
f.bar = 42, 17
#=> [[42,17], nil]
#=> No block
f.send(:bar=,42,17) do |x|
puts "x is #{x.inspect}"
end
#=> [42, 17]
#=> x is "hi"
Another way in which these methods are special is that when called with the syntax sugar they evaluate to the right hand value, not the return value of the method:
class Foo
def bar=(a)
return 17 # really explicit
end
end
f = Foo.new
x = (f.bar = 42)
p x
#=> 42
x = f.send(:bar=,42)
p x
#=> 17
It's not so much that the methods themselves are special, but more to do with how Ruby deals with assignments (like foo = bar). First the right hand side is evalulated, then the left hand side is evaluated and the appropriate action is taken. If the left hand side is an object attribute, then the appropriate setter method is called.
So in your example:
Dummy.new.foo = 1 { |x| x }
First ruby tries to evalulate 1 { |x| x }, which is what causes the syntax error.
Dummy.new.foo=something doesn't actually mean "call the method named foo=", but actually means something more like "evalualate something, and then determine what `Dummy.new.foo is, and if it looks like an object attribute, add = to the name and call that method". This is why Dummy.new.foo= and Dummy.new.foo = both work the same way.
You can call these methods using send, and can pass a block with this:
Dummy.new.send "foo=", 2 do
puts "HI"
end
This is because with send you can explicitly name the method you want to call.
Of course the end result is that methods ending in = seem to have some "special" behaviour you need to be aware of, but it might be useful to understand what's actually going on.