Po co ludzie definiuja metodę o nazwie identycznej jak klasa?

We wspomnianym w innym wątku gemie Progress widzę taki kawałek kodu:

module Kernel def Progress(title = nil, total = nil, &block) Progress.start(title, total, &block) end private :Progress end
Widywałem to już przed laty, chyba w Hpricocie, czy gdzieś tam.

Pytanie: po co ludzie to robią?

Definiowanie metody, która (zgodnie z konwencją) jest stałą (bo z wielkiej litery leci) według mnie jest niezbyt bezpiecznym, a na pewno rujnuje czytelnośc kodu. Ale skoro to robią, to korzyści muszą być większe niż ryzyko.

Ktoś wie jakie są “za” i “przeciw” czegoś takiego?

Przykład z drugiej strony:

Array(1) => [1] Array([1,2,3]) => [1,2,3] Hash[[[1,2],[2,3]]] => {1=>2, 2=>3}
Korzystałeś? No to już wiesz po co. Choć też specjalnie za tym nie przepadam.

Korzystam, ale tylko z Hash::[] - przy okazji - to nie jest funkcja, tylko operator, więc się nie liczy. Czasami parser głupiał i literał hasha traktował mi jak blok.

Ale nadal mnie nie przekonuje - gdzie tu korzyść? Jeżeli mam linijkę

Progress

to czy to jest wywołanie funkcji bez parametrów, czy może zwraca mi to stałą? Konwencja mówi, że wszystko co się zaczyna od dużej litery to stała. A tu mam tej konwencji naruszenie…

Podejrzewam, że nie ma na to odpowiedzi, bo powinien się wypowiedzieć ktoś, kto takie rzeczy stosuje w swoim kodzie. Pewnie wszyscy się zgodzimy, że nie lubimy, ale ludzie to robią :slight_smile:

Jest to stała przechowująca funkcję.

No operator, tyle że w Rubim operatory to też funkcje (a właściwie metody) :wink: Tzn. zawsze możesz to wywołać jako Hash..

[quote=Arsen7]Ale nadal mnie nie przekonuje - gdzie tu korzyść? Jeżeli mam linijkę

Progress

to czy to jest wywołanie funkcji bez parametrów, czy może zwraca mi to stałą? Konwencja mówi, że wszystko co się zaczyna od dużej litery to stała. A tu mam tej konwencji naruszenie…

Podejrzewam, że nie ma na to odpowiedzi, bo powinien się wypowiedzieć ktoś, kto takie rzeczy stosuje w swoim kodzie. Pewnie wszyscy się zgodzimy, że nie lubimy, ale ludzie to robią :)[/quote]
Zauważ, że samo wywołanie Progress nie ma sensu - musi być blok.

Ale zgodzę się, że to nie ma specjalnego uzasadnienia. Tzn. może ktoś po prostu nie chciał robić tak ja w benchmarku:

[code]Benchmark.benchmark{|bm| bm.report { sleep 0.1}}

zamiast tego w progress

Progress{ sleep 0.1}[/code]
Zaoszczędzenie powtarzających się słów. Kwestia gustu.

To gdzie się podziała stała przechowująca klasę?

W zasadzie zaraz wezmę irb i się dowiem, ale generalnie każda konstrukcja, przy której trzeba się zastanawiać nad takimi rzeczami jest Zła. :slight_smile:

No operator, tyle że w Rubim operatory to też funkcje (a właściwie metody) :wink: Tzn. zawsze możesz to wywołać jako Hash..[/quote]
Tak, prawda, ale chodzi mi o to, że w tym wypadku nie ma niejednoznaczności składni: ‘Hash[x]’ jest od razu widoczne jako .send :[] wywołany na obiekcie Hash - to dopiero nawiasy, a dokładniej to ich opcjonalny brak zaczyna powodować problem niejednoznaczności. To dlatego twierdzę, że ten przypadek nie jest problematyczny.

A jednak sens już może mieć taka konstrukcja:

def klasa_do_czegostam if costam Xyzzy else Progress end end
I może być problem, jeżeli to coś mi zwróci nie obiekt klasy, tylko wynik działania metody o tej nazwie. A czy jest reguła, która mówi co ruby zrobi w takiej sytuacji? No bo jeśli nie ma, to jest to nieudokumentowany hack i w każdej chwili może zacząć działać odwrotnie. No i kto nie ma wtedy dobrych testów… :smiley:

Dobra, mam. Świat nadal się kręci, bo ruby działa z sensem. Jeżeli za ‘Progress’ użyję nawiasów, parametrów albo bloku, czyli intencja wywołania metody jest jasna, ruby wywołuje metodę. W przeciwnym wypadku traktuje to jako stałą.

To trochę podobnie jak ze zmiennymi lokalnymi, choć w tym przypadku jeżeli istnieje metoda zaczynająca się od dużej litery, to przy braku takiej stałej ruby metody już szukał nie będzie i grzecznie rzuca błędem. Znaczy - jeśli nie dorzucę nawiasów, parametru albo bloku.

Poza Kernel.

Nie do końca się zgodzę. :slight_smile:

Wszystko dziedziczy z Kernel (w uproszczeniu), a ja będąc w Kernel, na przykład w irb mam dostęp i do funkcji i do klasy (jak to sprawdziłem powyżej). To znaczy, że jednak wchodzi tu parser i raz moje wpisane literki zamienia sobie na obiekt znaleziony wśród zdefiniowanych stałych, a innym razem wśród metod.

Wydaje mi się :wink: , że “wszystko” dziedziczy po Object, do którego włączany jest Kernel.
Przecież możesz użyć metody z duża literą:

Method names, however, are allowed to start with capital letters. This can lead to confusion, as the example below shows:

Constant = 10 def Constant 11 end Now Constant is 10, but Constant() is 11.
Masz kilka takich metod w Kernelu, jak String, Array itp. Pozwala to na stworzenie “skrótu” w składni dla tworzenia obiektów tego typu.
Zobacz przykład:

[1, 2, 3].map(&:to_s)

a teraz w drugą stronę

["1", "2", "3"].map(&method(:Integer))

Wydaje mi się :wink: , że “wszystko” dziedziczy po Object, do którego włączany jest Kernel.[/quote]
Dlatego napisałem, że w uproszczeniu. Nie zawsze szczegóły są ważne :slight_smile:

[quote]Przecież możesz użyć metody z duża literą:
http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/[/quote]
Ja wiem, że mogę. Chodziło mi o to, czy ktoś widzi jakieś ważne powody, żeby poświęcić czytelność kodu (przez unikanie tego “confusion”) na rzecz pozbycia się paru znaków.

Czy uważasz, że ten skrót w składni jest tego wart?

[quote]Masz kilka takich metod w Kernelu, jak String, Array itp. Pozwala to na stworzenie “skrótu” w składni dla tworzenia obiektów tego typu.
Zobacz przykład:

 [1, 2, 3].map(&:to_s)

a teraz w drugą stronę

["1", "2", "3"].map(&method(:Integer))

http://andrewjgrimm.wordpress.com/2011/10/03/in-ruby-method-passes-you/[/quote]
Ja bym napisał

["1","2","3"].map(&:to_i)

Chyba bardziej czytelne. :slight_smile:

A jeśli rzeczywiście potrzebne by było coś bardziej złożonego, to raczej bym zdefiniował “normalną” metodę w klasie, albo użyłbym “normalnego” bloku.

["1","2","3"].map {|i| Integer.new(i)}

Też 100x bardziej czytelne.

Tak więc, gdzie zysk z takich funkcji? Nie warto, czy po prostu czegoś nie dostrzegam? Na razie wychodzi mi, że rzeczywiście nie ma w tym jakichś ukrytych super plusów i definiując takie funkcje ludzie po prostu przeginają nieco z mikrooptymalizacją.

Jeszcze zależy, o której wersji mówimy :wink:

[code]>> Object.superclass
=> BasicObject

BasicObject.superclass
=> nil

[/code]

Jest krócej. Innych zalet nie widzę. W FactoryGirl też kiedyś można było zrobić Factory(:post), teraz już chyba się nie da. Ja też wolę metody klasowe, np. Array.wrap(something) jest dużo bardziej czytelne niż Array(something) zakładając, że ktoś patrzy pierwszy raz na obie metody.

Zgadzam się, ale jest to kwestia gustu, a o gustach się nie dyskutuje (podobno).