Metaprogramming & higher-order functions dualis
От: __lambda__ Россия http://zen-hacker.blogspot.com/
Дата: 18.03.11 06:39
Оценка:
Я тут немного пофилософствовал на тему метапрограммирования на макросах и его дуализма с функциями высшего порядка. Давайте по порядку, начнем с самого начала. А началось все с того, что начал я изучать очень клевый язык, который называется Лисп (если быть точнее, то это семейство языков). Одним из его мощнейших свойств являются макросы. Суть в том, что с помощью них, мы можем избежать дублирования кода. А также строить полноценные мини-языки (eDSL) и т.д. Но давайте ближе к делу, то есть, ближе к коду. Рассмотрим такой участок кода на гипотетическом языке похожем на Си:
try
{
    file.assign(fname);
    file.open();
    // do something
    file.process();
}
finally
{
    file.close();
}

Такой код может часто встречаться в программе и мы вынуждены будем каждый раз дублировать его. Сразу видно, что "переменными" тут являются file, fname и код после комментария do something. Все остальное, всегда будет одним и тем же, открытие файла, обработка исключений и закрытие файла.

Что могут в данном случае нам предоставить макросы Лиспа? Давайте рассмотрим гипотетический код на языке похожем на Лисп:
(with-open-file (file fname)
    (file.process))

Этот код аналогичен вышеприведенному коду. Здесь мы видим, что тут у нас остались так называемые "переменные" участки кода. А все остальное скрывается под капотом макроса with-open-file, то есть, открытие/закрытие файла, обработка исключения все равно выполняются. Мы можем вызывать этот макрос с другими "переменными":
(with-open-file (in-file "input.txt")
    (parse-data in-file)
    (print "file parsed"))

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

И достигалось бы это с помощью примерно такого гипотетического макроса:
(defmacro with-open-file (file fname rest &body)
    `(try
         (,file.assign ,fname)
         (,file.open)
         ,@body)
         (finally
             (,file.close))))

Пишу от балды, похожем на Common Lisp синтаксисе. Главное, чтобы передавало суть, основную идею.

То есть, с помощью макросов, мы можем избавиться от дублирующегося кода. Более того, вводим новую синтаксическую конструкцию with-open-file, обладающей заданной нами семантикой.

И тут я глубоко призадумался, можно ли в других языках, избавляться от такого вида дублирования кода или мы обречены писать дублирующийся код?

Ответ, да. Мы можем это сделать, но с помощью другого механизма, который называется функции высшего порядка. Это когда, функции, являются объектами первого класса, то бишь, мы функции можем хранить в переменных, передавать как параметр в другую функцию и возвращать из функции как результат. Давайте посмотрим, как можно было сделать все то же самое, что мы делали с помощью макросов, будь у нас функции высокого порядка, на гипотетическом языке похожем на Си, у которого есть функции высокого порядка:
var open-file = function (file, fname, action) {
                             try {
                                 file.assign(fname);
                                 file.open();
                                 action();
                             }
                             finally {
                                 file.close();
                             }

open-file("input1.txt", in-file1, function () { in-file1.process(); });
open-file("input2.txt", in-file2, function () { in-file2.process(); });

Таким образом мы можем избавиться от дублирующегося кода с помощью функций высокого порядка без применения макросов. Я тут у себя даже попробовал примерно нарисовать небольшой eDSL'чик. Вроде получается, например, попробовал ввести аналог цикла while:
var while = function (i, cond, body) {
                       loop {
                           if (cond(i))
                               body(i);
                           else
                               break;
                       }
                   }

var i = 0;
while(i, func (i) { i < 10 }, func (i) { print(i); });


Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.
Computer science is no more about computers than astronomy is about telescopes (c) Edsger Dijkstra
Re: Metaprogramming & higher-order functions dualis
От: Jack128  
Дата: 18.03.11 06:48
Оценка: 3 (2) +2
Здравствуйте, __lambda__, Вы писали:

___>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.


Если в языке есть только функции, то возможно(не уверен) на ФВП и можно повторить все, что можно сделать на макрасах. Но как только в языке появляется еще что нить (ну например типы) — то все. макрос может создать новый тип, а функция — нема.
Re: Metaprogramming & higher-order functions dualis
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.03.11 06:53
Оценка: 4 (1) +1
Здравствуйте, __lambda__, Вы писали:
___>Жду ваших мыслей по этому поводу.
Для чисто-ФП языка всё будет работать. См. например серию Барта про построение всех высокоуровневых конструкций из подручных средств:
http://bartdesmet.net/blogs/bart/archive/2009/07/14/bart-s-control-library-not-what-you-think-it-is-part-2.aspx (и по ссылкам на части 1 и 0)

Но вот, к примеру, прикрутить к классу дефолтную реализацию некоего интерфейса при помощи чисто ФВП не выйдет. А макросы способны и на это.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Metaprogramming & higher-order functions dualis
От: 0x7be СССР  
Дата: 18.03.11 06:56
Оценка: 4 (1)
Здравствуйте, __lambda__, Вы писали:


___>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.

Разбери, плз, такой прецедент: есть необходимость проверять параметры функций на не-null`овость. Для этого в начале каждой функции приходится писать однообразный код вида:
public SomeType MyFunction(ReferenceType1 param1, ReferenceType2 param2, ValueType param3)
{
  Guard.CheckNotNull(param1, "param1");
  Guard.CheckNotNull(param2, "param2");
  ...
}

Тупой и однообразный код, который легко генерируется макросами (на том же Немерле).
Как через ФВП решить эту задачу? Если ещё придумаешь удовлетворительное решение на C#, то я тебе бутылку коньяка куплю,
ибо задача из реального проекта, где этот код в больших количествах приходится писать ручками
Re: Metaprogramming & higher-order functions dualis
От: WolfHound  
Дата: 18.03.11 07:12
Оценка:
Здравствуйте, __lambda__, Вы писали:

___>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.

Вот макра попроще.
Вот посложнее. Пример.
Вот макра которая переписывается в вызовы ФВП. Ибо если использовать ФВП напрямую получается такая грязюка что аж жуть...
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: Metaprogramming & higher-order functions dualis
От: dotneter  
Дата: 18.03.11 07:34
Оценка:
Здравствуйте, __lambda__, Вы писали:

___>Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка.

define-struct
___>Жду ваших мыслей по этому поводу.
В чем то они конечно похожи, функция это переиспользуемый статический кусок кода, макрос динамический, там где динамику можно заменить функцией, по сути они равны. C фвп можно более менее оперировать на уровне потока вычисления, но до уровня типов вы с ними не доберетесь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[2]: Metaprogramming & higher-order functions dualis
От: Temoto  
Дата: 18.03.11 08:01
Оценка:
___>>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.
0>Разбери, плз, такой прецедент: есть необходимость проверять параметры функций на не-null`овость. Для этого в начале каждой функции приходится писать однообразный код вида:
0>
0>public SomeType MyFunction(ReferenceType1 param1, ReferenceType2 param2, ValueType param3)
0>{
0>  Guard.CheckNotNull(param1, "param1");
0>  Guard.CheckNotNull(param2, "param2");
0>  ...
0>}
0>

0>Тупой и однообразный код, который легко генерируется макросами (на том же Немерле).
0>Как через ФВП решить эту задачу? Если ещё придумаешь удовлетворительное решение на C#, то я тебе бутылку коньяка куплю,
0>ибо задача из реального проекта, где этот код в больших количествах приходится писать ручками

А разве в шарпе не было полноценных контрактов через [аттрибуты] ?
Re[2]: Metaprogramming & higher-order functions dualis
От: deniok Россия  
Дата: 18.03.11 08:23
Оценка: +1
Здравствуйте, Jack128, Вы писали:


J>Если в языке есть только функции, то возможно(не уверен) на ФВП и можно повторить все, что можно сделать на макрасах. Но как только в языке появляется еще что нить (ну например типы) — то все. макрос может создать новый тип, а функция — нема.


Зависимые типы именно для этого и созданы — они позволяют создавать типы из выражений языка (термов), то есть строить функции из термов в типы.
Re[2]: Metaprogramming & higher-order functions dualis
От: dotneter  
Дата: 18.03.11 08:41
Оценка: +2
Здравствуйте, Jack128, Вы писали:

J>Если в языке есть только функции, то возможно(не уверен) на ФВП и можно повторить все, что можно сделать на макрасах.

Макросы это кодогенерация, как с помощью фвп сгенерировать например по wsdl обертку к сервису?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[2]: Metaprogramming & higher-order functions dualis
От: __lambda__ Россия http://zen-hacker.blogspot.com/
Дата: 18.03.11 09:53
Оценка: -1 :)
Здравствуйте, Jack128, Вы писали:

J>Если в языке есть только функции, то возможно(не уверен) на ФВП и можно повторить все, что можно сделать на макрасах. Но как только в языке появляется еще что нить (ну например типы) — то все. макрос может создать новый тип, а функция — нема.


А как с помощью макроса можно создать новый тип?

Я например, новый тип создавал с помощью одних только функций в Common Lisp'e:
(define make-pos (row col)
  (cons row col))

(define get-row (pos)
  (car pos))

(define get-col (pos)
  (cdr pos))

То есть, на одних лишь функциях создаю новую абстракцию pos, у которой есть конструктор make-pos, есть геттеры get-row и get-col.
Computer science is no more about computers than astronomy is about telescopes (c) Edsger Dijkstra
Re: Metaprogramming & higher-order functions dualis
От: hi_octane Беларусь  
Дата: 18.03.11 10:02
Оценка: 5 (1) :)
___>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.

Ты только одно малюсенькое применение макросов просто рассмотрел, вот и создалось ложное впечатление. У нас есть макрос который javascript файл из сети во время компиляции вытягивает, парсит, и по нему кучу кода строит, с типами, интерфейсами и т.п. Нужен для согласования с внешней системой. На ФВП такое не слепишь, кодогенератором заранее не сгенерируешь.
Вот ещё сейчас пишу макрос для проекта, который дополнительно ограничивает область видимости переменных:
class A
{  
  public func() : void
  {
    private c = 0;

    //код использующий c
  }
}

Из функции поле C выносится в класс, но доступно определение только в этой функции, остальным C не видно. Так что при всём желании нахачить ошибку не смогут. На ФВП такое попробуй повтори
Re[3]: Metaprogramming & higher-order functions dualis
От: 0x7be СССР  
Дата: 18.03.11 10:02
Оценка:
Здравствуйте, Temoto, Вы писали:

T>А разве в шарпе не было полноценных контрактов через [аттрибуты] ?

Есть, но не в шарпе, в виде сторонней библиотеки.
Однако вопрос не в этом, а как сделать сказанное через ФВП.
Re[4]: Metaprogramming & higher-order functions dualis
От: VoidEx  
Дата: 18.03.11 10:16
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Temoto, Вы писали:


T>>А разве в шарпе не было полноценных контрактов через [аттрибуты] ?

0>Есть, но не в шарпе, в виде сторонней библиотеки.
0>Однако вопрос не в этом, а как сделать сказанное через ФВП.

А в чём вообще вопрос? В том, как из идентификатора получить его имя в виде строки или в том, чтобы не писать x второй раз в foo x = { notnull x; ... }?
foo = notnull $ \x -> notnull $ \y -> \z ->
    x + y + z


Красота сомнительна, конечно, но речь и не о ней, как я понял. Иначе достаточно было бы попросить сделать в ФВП
var camelCased(some_name) = 34; // SomeName = 34;
SomeName = 22;
Re[2]: Metaprogramming & higher-order functions dualis
От: __lambda__ Россия http://zen-hacker.blogspot.com/
Дата: 18.03.11 10:24
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Разбери, плз, такой прецедент: есть необходимость проверять параметры функций на не-null`овость. Для этого в начале каждой функции приходится писать однообразный код вида:

0>
0>public SomeType MyFunction(ReferenceType1 param1, ReferenceType2 param2, ValueType param3)
0>{
0>  Guard.CheckNotNull(param1, "param1");
0>  Guard.CheckNotNull(param2, "param2");
0>  ...
0>}
0>


Не знаю, я бы тут мудрить не стал бы, а тупо написал бы функцию хелпер, типа:
void CheckNotNulls(params object[] args)
{
  // и тут бы дергал для каждого параметра CheckNotNull
}

Итого, твой пример превратился бы в:
public SomeType MyFunction(ReferenceType1 param1, ReferenceType2 param2, ValueType param3)
{
  Guard.CheckNotNulls(param1, param2);
  ...
}

Вместо N строк на каждый параметр, мы теперь вызываем одну функцию CheckNotNulls, но зато с N параметрами, а это все равно 1 строка.

А так, да. Кажется тут ФВП не особо помогут.
Computer science is no more about computers than astronomy is about telescopes (c) Edsger Dijkstra
Re[3]: Metaprogramming & higher-order functions dualis
От: 0x7be СССР  
Дата: 18.03.11 10:29
Оценка:
Здравствуйте, __lambda__, Вы писали:

___>Вместо N строк на каждый параметр, мы теперь вызываем одну функцию CheckNotNulls, но зато с N параметрами, а это все равно 1 строка.

А названия параметров откуда этот метод будет брать?
Не, есть один обходной вариант, но он завязан на использование Reflection и нецелевого использования анонимных типов.
Re: Metaprogramming & higher-order functions dualis
От: __lambda__ Россия http://zen-hacker.blogspot.com/
Дата: 18.03.11 10:32
Оценка: +1
Здравствуйте, __lambda__, Вы писали:

___>Итого, я выдвигаю такую гипотезу, что метапрограммирование на макросах и функции высшего порядка равносильны. И что, то же самое, мы можем делать в рамках нашего языка, не меняя его синтаксис. Для опровержения моей гипотезы, достаточно привести пример такого макроса, который не возможно повторить на функциях высшего порядка. Жду ваших мыслей по этому поводу.


Действительно, оказывается есть такие примеры, где макросы мощнее, чем ФВП. Моя гипотеза опровергнута.
Computer science is no more about computers than astronomy is about telescopes (c) Edsger Dijkstra
Re[4]: Metaprogramming & higher-order functions dualis
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.03.11 10:33
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Не, есть один обходной вариант, но он завязан на использование Reflection и нецелевого использования анонимных типов.

Он не один. Есть вариант с нецелевым использованием Expression Tree.
Re[4]: Metaprogramming & higher-order functions dualis
От: __lambda__ Россия http://zen-hacker.blogspot.com/
Дата: 18.03.11 10:36
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>А названия параметров откуда этот метод будет брать?


А я не понял, зачем тебе названия параметров? Но все равно, тебе никто не мешает передавать имена параметров тоже CheckNotNulls(param1, "param1", param2, "param2"); а потом уже из этой хелперной функции попарно читать и передавать параметр и его имя в конкретную CheckNotNull.

0>Не, есть один обходной вариант, но он завязан на использование Reflection и нецелевого использования анонимных типов.


Reflection — разве не тормознутый?
Computer science is no more about computers than astronomy is about telescopes (c) Edsger Dijkstra
Re[5]: Metaprogramming & higher-order functions dualis
От: 0x7be СССР  
Дата: 18.03.11 10:44
Оценка:
Здравствуйте, samius, Вы писали:

S>Он не один. Есть вариант с нецелевым использованием Expression Tree.

И так можно, да.
Re[5]: Metaprogramming & higher-order functions dualis
От: 0x7be СССР  
Дата: 18.03.11 10:45
Оценка:
Здравствуйте, __lambda__, Вы писали:

0>>А названия параметров откуда этот метод будет брать?

___>А я не понял, зачем тебе названия параметров? Но все равно, тебе никто не мешает передавать имена параметров тоже CheckNotNulls(param1, "param1", param2, "param2"); а потом уже из этой хелперной функции попарно читать и передавать параметр и его имя в конкретную CheckNotNull.
Названия для того, что бы их в ArgumentException засунуть. Тогда в логе будет видно, что такая вот функйия получила на вход недопустимое значение в такмо-то параметре.

0>>Не, есть один обходной вариант, но он завязан на использование Reflection и нецелевого использования анонимных типов.

___>Reflection — разве не тормознутый?
Тормознутый, но там так хитро придумано, что эти траты одноразовые.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.