Agreed. Pattern matching and pipe operator were the two that I hoped for most.
The pipe operator implementation proposal that they team came up with was wrong though and I'm glad they didn't do it. We don't need alternate syntax for `.`, we need ability to chain arguments together in a syntactically pleasant way in a functional style so we don't have to write `first(second(third(arg)))` we can write `arg |> third() |> second() |> first` which is much cleaner and reads left to right like it should
Ruby-idiomatic pipe operator is Object#then. After a lot of design proposals and discussions it is more or less evident no solution other than method would integrate naturally with the rest of the code.
So it is just `arg.then{ third(_1)}.then{ second(_1)}.then{ first(_1)}`
Would've been a bit more concise with method references, almost introduced in 2.7, but alas.
(But, well, people tend to want "something that looks like operator, preferably something that looks exactly like |>" and reject every other possibility)
This would be more elegant if there was a better way to do `&object.method(:method_name, …)`. Unbound method support has been around for a long time but converting a method call with arguments to a prod is still not simple to do… unless you start currying methods and writing obscure code.
Yes, that's what I referred to. Before 2.7, object&.:method was almost merged (or rather merged and reverted) because Matz had second thoughts about its uglyness... Which is not completely untrue, but not having a concise way for referring to a method is irritating.
That still wouldn't have solved passing additional args, so maybe { object.method_name(_1, args)} is the next best thing. Though it perceives non-atomic due to wrapping block.
I’ve had a lot of fun with Ruby’s support for functional paradigms but that stuff is unlikely to get through code review when the status quo tends towards ‘Clean Code’ style OOP over-abstraction.
Ruby being a type 2 lisp is a fun one - creating a class and and a factory function with the same name, with argument forwarding:
class Animal; …; end
def Animal(…); Animal.new(…); end
Refinements would be more useful if you could expose the refinements, but currently you can't.
module HashExts
refine Hash do
def symbolize_values = transform_values { _1.to_sym }
end
end
module Test
using HashExts
def self.new_h = Hash.new
end
puts Test.new_h.symbolize_values
# => undefined method `symbolize_values' for {}:Hash (NoMethodError)
Yeah, I feel like there was a bit of an expectation mismatch around that. `using` makes a lot more semantic sense than `include` or `extend` in a lot of cases but it didn’t play out that way and we’re still living with the unusual convention of making ‘x-able’s and writing ‘concerns’. Not to mention that they were file-scoped and not lexical in their first version so had limited utility for library devs.
As far as I know the teething issues around refinements are ironed out but they remain an obscurity.
The pipe operator implementation proposal that they team came up with was wrong though and I'm glad they didn't do it. We don't need alternate syntax for `.`, we need ability to chain arguments together in a syntactically pleasant way in a functional style so we don't have to write `first(second(third(arg)))` we can write `arg |> third() |> second() |> first` which is much cleaner and reads left to right like it should