1

Short:

Is there a way to create extended Array class named A, that supports full method chain with map, sort, sort_by and new method word and does not harm Array class?

Example:

 [
    A.new(a).word,
    A.new(a).sort.word,
    A.new(a).sort_by(&:-@).word,
    A.new(a).map(&:chr).sort.map(&:ord).word
  ]

Long story:

I solved this kata and created code that extends Array class with new word method:

class Array
  def word
    [*self[0..1],*self[-2..-1]].map(&:chr).join
  end
end
def sort_transform(a)
  [
    a.word,
    a.sort.word,
    a.sort_by(&:-@).word,
    a.map(&:chr).sort.map(&:ord).word
  ].join ?-
end

Then I thought this is not a good idea to add method for such kind of base classes. And I tried to implement new class that inherited all behavior from an Array.

class A < Array
  def word
    [*self[0..1],*self[-2..-1]].map(&:chr).join
  end
end

This addition breaks my code, because map, sort, sort_by returns an Array instance: A.new([1,2,3]).sort.class # Array. And Array does not understand a word method. And instead of A.new(a).sort.word I have to encapsulate part of chain into a A.new constructor: A.new(a.sort).word. That's definitely breaks pure method chain.

Is it possible to extend Array such way to reach pure method chains like this; A.new(a).sort.word?

When I tried to write line this: class A < Array

def word
    [*self[0..1],*self[-2..-1]].map(&:chr).join
  end
  def sort
    A.new(self.sort)
  end
end

This brings me main.rb:8:in 'sort': stack level too deep (SystemStackError) Finally already writing this lines I found a way to avoid deep stack: converting self to Array and then again convert it to A.

class A < Array
  def word
    [*self[0..1],*self[-2..-1]].map(&:chr).join
  end
  def sort
    A.new(self.to_a.sort)
  end
end

So is It an only way to implement such extension?

6
  • The article was given to me words.steveklabnik.com/beware-subclassing-ruby-core-classes And developer who gave me this article told that instances cast back to Array in method chain in Ruby because of C code. It implements for speed. Commented Jul 4, 2019 at 6:06
  • 1
    I'd use a refinement on Array instead. Why does no-one use refinements? Commented Jul 4, 2019 at 6:18
  • @Amadan this why I wrote this question :) To get information you gave. Commented Jul 4, 2019 at 6:25
  • Why would you add an instance method to Array, subclass Array or refine Array. Why not just define a method word with one argument for the given array? Commented Jul 4, 2019 at 21:19
  • @CarySwoveland: Why would you not refine? That's why they're for. Why refine over a function? Because it chains better, and chaining reads more naturally since it linearly represents causality. f.each_line.map(&:length).max has flow. find_max(get_lines(f).map(&:length)) goes back and forth and incurs a higher mental load. The non-parallelism between syntax and execution order is one of the reasons why I dislike Python: why is len a function but lower a method but map a function but join a method on string but sorted a function but sort a method... And comprehensions... >.< Commented Jul 10, 2019 at 10:27

1 Answer 1

4

If you ever want to monkey-patch a core class but you think it's icky, you should remember that refinements are made just for this scenario.

module ArrayWithWord
  refine Array do
    def word
      [*self[0..1],*self[-2..-1]].map(&:chr).join
    end
  end
end

class WordTest
  using ArrayWithWord

  # [].word works here
  def self.sort_transform(a)
    [
      a.word,
      a.sort.word,
      a.sort_by(&:-@).word,
      a.map(&:chr).sort.map(&:ord).word
    ].join ?-
  end
end

WordTest.sort_transform(%w(foo bar baz quux))
# => "fbbq-bbfq-bbfq-bbfq"

# [].word doesn't work here
[].word
# => NoMethodError (undefined method `word' for []:Array)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.