LINUX.ORG.RU

Скрестить ужа с ежём: first-class функции vs методы, как частный вид функций

 , , ,


1

1

Навеяно чьими-то сообщениями в темах про ООП, что прочел давно, но не было времени сформулировать проблему...

Итак предположим, что в нашем языке функции - это объекты, а объекты могут иметь методы. У функций есть специальный метод apply(call, operator(), etc.), который вызывается при вызове функции. В этом случае получается, что методы не могут являться first-class объектами и разновидностью функций, т.к. тогда у них должен быть метод apply, который в свою очередь должен иметь метод apply, который и т.д. и т.д.

Решения у нас могут быть примерно такие:

- или мы делаем как в крестах и получаем не first-class функции/методы, но имеем возможность сделать «объект-функцию»(в том числе через лямбды), если потребуется;

- или мы делаем как в scala и получаем довольно стройную систему, но отказываемся от простой очевидной концепции метода как частного вида функции и, как следствие, от first-class методов;

- или мы делаем «функции» особым видом объектов, который нельзя сделать в обход механизма создания функций(определив для объектов какого-то типа apply или operator());

Я правильно понимаю? Или можно таки разорвать цепочку красиво?


Ответ на: комментарий от loz

y = 1

а это локальная переменная? Что, в питоне нет спец синтаксиса для определения локальных?

anonymous
()
Ответ на: комментарий от anonymous

который как бы и объект, но вызвать его можно напрямую, без какого-либо метода call.

это как например?

anonymous
()
Ответ на: комментарий от anonymous

Локальные по-дефолту, есть синтаксис для определения глобальных:

In [10]: y
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-009520053b00> in <module>()
----> 1 y

NameError: name 'y' is not defined

In [11]: def g(x):
   ....:     global y
   ....:     return eval(x)
   ....: 

In [12]: g("y")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-12-ffe633ce853f> in <module>()
----> 1 g("y")

<ipython-input-11-b3c04a1692eb> in g(x)
      1 def g(x):
      2     global y
----> 3     return eval(x)
      4 

<string> in <module>()

NameError: name 'y' is not defined

In [13]: y = 7

In [14]: g("y")
Out[14]: 7


loz ★★★★★
()
Ответ на: комментарий от anonymous

Конкретно, как? У тебя есть ссылка — метод — ты получил. Как ты получишь его, если нет ссылки?

anonymous
()

Был пьян. Извиняюсь за возможную невнятность и за «ежём».

Спасибо всем отписавшимся, читаю.

forCe
() автор топика
Ответ на: комментарий от monk

То есть функция может быть определена как функция. Тогда у неё встроенный метод apply(). Может быть определена с явным методом apply. Который также может быть определён либо как функция, либо как объект.

Тут понятно. Но не вижу решения проблемы.

Также и вызов. Можно писать f(x, y), можно f.apply(x, y), можно f.apply.apply(x, y), ..., f.apply.apply.apply.apply.apply.apply(x, y). Они все эквивалентны.

Это аксиома? =) Если нет, то почему они эквиваленты?

forCe
() автор топика
Ответ на: комментарий от loz

Правда лямбдой не написать

пфф

>>> f = lambda x: locals().update(dict(y=1)) or eval(x)
>>> f("y")
1
>>> 

Virtuos86 ★★★★★
()
Ответ на: комментарий от user_id_68054
$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 'world'
>>> def f(): print('hello {}!'.format(x))
... 
>>> f()
hello world!
>>> type(f).__call__(f)
hello world!
>>> type(type(f).__call__).__call__(type(f).__call__, f)
hello world!
>>> type(type(type(f).__call__).__call__).__call__(type(type(f).__call__).__call__, type(f).__call__, f)
hello world!
>>> type(type(f).__call__).__call__ == type(type(type(f).__call__).__call__).__call__
True
>>> type(type(f).__call__).__call__ is type(type(type(f).__call__).__call__).__call__
True
>>> 

Хм. Получается, что __call__ на определенном уровне один и тот же объект, но количество аргументов определяет поведение?

Немного переписал пример.

def f(): print "aaa"

f0 = f
f1 = type(f0).__call__
f2 = type(f1).__call__
f3 = type(f2).__call__
f4 = type(f3).__call__

print f1 == f0
print f2 == f1
print f3 == f2
print f4 == f3

Имеем:

False
False
True
True
Далее
f1(f0)
f2(f1, f0)
f3(f2, f1, f0)
f4(f3, f2, f1, f0)
Получаем ожидаемое
aaa
aaa
aaa
aaa
Вводим fn = f3 и повторяем вызовы
fn = f3
fn(f3, f2, f1, f0)
fn(f2, f1, f0)
fn(f1, f0)
Все работает(хотя f2 и f3 не равны)
aaa
aaa
aaa
И только при вызове
fn(f0)
Получаем
descriptor '__call__' requires a 'wrapper_descriptor' object but received a 'function'

forCe
() автор топика
Ответ на: комментарий от anonymous

Но в scala методы - первоклассные объекты. Что-то у тебя не сходится.

Точно? Мне казалось, что там создается method value в нужных местах, т.е. просто компилятор упрощает нам жизнь, создавая объекты-функции самостоятельно в нужных местах, а сами методы объектами не являются. Возможно, я ошибаюсь.

forCe
() автор топика
Ответ на: комментарий от bogus_result

В общем, резюме такое - чтоб рассуждать об объектах, методах как объектах, объектно-ориентированном программировании, моделях ООП и тд. нужно иногда читать умные книги. Тот вопрос, который в этом топике вызвал столько споров, прекрасно описан в книге CTMCP.

Спасибо за наводку. Давно хотел прочесть, но как-то все не доходили руки.

В какой главе описан данный вопрос?

forCe
() автор топика
Ответ на: комментарий от forCe

хотя f2 и f3 не равны

Ошибся, они равны как раз, так что все ожидаемо.

forCe
() автор топика
Ответ на: комментарий от forCe

Предупрежу сразу, там в основе ядра языка замыкания, а классы делаются через синтаксический сахар к замыканиям. Поэтому придётся почитать больше одной главы. Непосредственно эта тема в главе 7.

bogus_result
()
Ответ на: комментарий от bogus_result

Ясно, почему в вашем примере f2, f3 и f4 это одно и то же? И почему f1 отличается от них.

Как я понимаю, f1 - это, собственно метод __call__ класса/типа функции. Тип он имеет какой-то специальный - wrapper_descriptor. И вот на его уже метод __call__ мы ссылаемся через f2, тип он имеет, опять же, wrapper_descriptor и потому f3, f4 и т.д. будут ссылаться на тот же самый __call__.

forCe
() автор топика
Ответ на: комментарий от forCe

Точно? Мне казалось, что там создается method value в нужных местах, т.е. просто компилятор упрощает нам жизнь, создавая объекты-функции самостоятельно в нужных местах, а сами методы объектами не являются.

А какая разница?

anonymous
()
Ответ на: комментарий от forCe

да. эт ты интересный эксперимент затеял :-)

думаю вся разгадка в том что:

>>> type(f)
<class 'function'>
>>> type(type(f))
<class 'type'>
>>> type(type(type(f)))
<class 'type'>
>>> type(type(type(type(f))))
<class 'type'>
>>> type(type(f)) == type(type(type(f)))
True
>>> type(type(f)) is type(type(type(f)))
True

по этой причине и __call__ на определённом уровне равны.. (хотя теоретически ведь они могли бы быть и не равны)

user_id_68054 ★★★★★
()
Ответ на: комментарий от bogus_result

Метод объекта также является объектом

Метод НЕ является ни функцией, ни объектом, поскольку он вообще говоря не существует вне объекта или класса. Метод - это алгоритм. Правило. Формула. Нельзя просто вызвать запустить алгоритм без данных, нет метода вне контекста, явного или неявного.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

Если в нашей системе методы не являются ни объектом, ни функцией, то и проблемы изначальной нет.

forCe
() автор топика
Ответ на: комментарий от no-dashi

Метод НЕ является ни функцией, ни объектом, поскольку он вообще говоря не существует вне объекта или класса.

Бред, он вполне себе существует и может быть применен к списку аргументов, первый из которых (в «классическом» ооп) экземпляр подходящего класса.

Более того, в некоторых реализациях ООП методы существуют и описываются совершенно отдельно от описания классов, например в clos.

Так что метод это и функция и объект, как и все остальное.

loz ★★★★★
()
Ответ на: комментарий от loz

он вполне себе существует и может быть применен к списку аргументов, первый из которых (в «классическом» ооп) экземпляр подходящего класса.

Это и называется «в контексте объекта». Без привязки к объекту метод вызвать нельзя.

Более того, в некоторых реализациях ООП методы существуют и описываются совершенно отдельно от описания классов, например в clos.

Просто вопрос синтаксиса. Пригодным к использованию (к вызову) метод станет только когда он будет связан с объектом.

no-dashi ★★★★★
()
Ответ на: комментарий от bogus_result

как вычисляется какой-нибудь простой случай вычисления G.f() и как это завязано на f.call()

Сахар над функциями и/или какой-нибудь object calculi.

http://www.cis.upenn.edu/~bcpierce/papers/index.shtml#Object-Oriented Program...

http://lampwww.epfl.ch/~odersky/papers/

http://lampwww.epfl.ch/~amin/dot/fool.pdf

нувыпонели, акторы

Какой-нибудь process calculi (π-calculus и т.п.), только они не факт что сводятся к функциям.

http://lampwww.epfl.ch/~cremet/publications/pilib.pdf

motto
()
Ответ на: комментарий от no-dashi

Пригодным к использованию (к вызову) метод станет только когда он будет связан с объектом.

Объект можно рассматривать как первый аргумент метода.

Твою фразу можно обобщить: пригодной к использованию (к вызову) функция станет только когда она будет связана с аргументами. Но это не мешает функции быть объектом.

anonymous
()
Ответ на: комментарий от no-dashi

Без привязки к объекту метод вызвать нельзя.

Функцию без аргументов тоже не вызовешь. Что, теперь и функция не объект?

anonymous
()
Ответ на: комментарий от no-dashi

Без привязки к объекту метод вызвать нельзя.

Значит функцию нельзя вызвать без привязки к аргументам? Функция не может быть пригодна к использованию без связи с аргументами? Ну фигня же.

loz ★★★★★
()
Ответ на: комментарий от no-dashi

Просто вопрос синтаксиса.

На самом деле нет, они хранятся и существуют независимо от объектов. Примерно как функция + в твоем языке существует независимо от чисел, и не связана с объектом, как например функция connect у сокета.

loz ★★★★★
()
Последнее исправление: loz (всего исправлений: 1)
Ответ на: комментарий от user_id_68054
#include <cstdio>
#include <functional>

struct Class {
    int data;
    void method() { printf("Class{%d}\n", data); }
};

int main() {
    using namespace std;
    Class object{1};
    object.method();
    auto functor = mem_fun_ref(&Class::method);
    functor(object);
    functor.operator()(object);
    mem_fun_ref(&decltype(functor)::operator()).operator()(functor, object);
}

последний вариант не стал писать :)

Ну и с равенствами наверно никак.

motto
()
Ответ на: комментарий от motto

В обджект-исчислениях не силен. Но в СТМСР нет ничего про обджект-исчисление. Я и спрашивал у ТС про его модель вычислений с объектами как элементарными единицами, поскольку не силен в этом.

bogus_result
()
Ответ на: комментарий от motto

Спасибо за годные ссылки на object и process calculi. Все хотел взяться, да руки не доходили.

bogus_result
()
Ответ на: комментарий от no-dashi

Это и называется «в контексте объекта». Без привязки к объекту метод вызвать нельзя.

Объект в этом смысле ничем не отличается от любого другого аргумента.

forCe
() автор топика
Ответ на: комментарий от motto

Здесь, как я говорил в стартовом сообщении, нюанс в том, что сам по себе method не является объектом. И у него нет никакого operator().

forCe
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.