LINUX.ORG.RU

Приходилось ли вам писать на Лиспе?


2

2

Ну, что ж, в Development так в Development, хотя Лисп давно перестал быть мемом одного лишь Development'а (и даже одного ЛОРа). Итак, сабж!

[ ] Да, профессионально и за деньги
[ ] Да, just for fun и для самообразования
[ ] Да, участвовал в opensource-проекте
[ ] Да, пилил скрипты Emacs/GIMP/AutoCAD/Lilypond etc.
[ ] Да, в рамках образовательной работы (лаба, курсовик, диплом)
[ ] Да, в рамках академической работы (диссертация, статья, монография)
[ ] Да, мне сказали, что лисперов любят девушки
[ ] Нет, но собираюсь
[ ] Нет, и не собираюсь
[ ] Вообще-то я Джон МакКарти, а вы кто такие?
[ ] в Советской России Лисп пишет на тебе!

Приветствуются развернутые ответы и верифицируемые пруфлинки. Например, на какую фирму работали, в каком конкретно opensource-проекте участвовали, какая была тема научной работы, помогло ли с девушками, и тому подобное. INB4 буквоедов: под «лиспом» подразумеваются все языки семейства: Scheme, CL, Clojure и прочие.

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

>> С момента написания книги некоторые вещи поменялись. Например, использование MOP'а стало гораздо более переносимым и поддерживается практически всеми лиспами.

Про МОР в книге почти ничего не сказано. Там речь идет только о стандартных фичах, которые остались неизменными. А так да, много чего вкусного появилось за последнее время.

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

нет, я не вижу тут загрузки и исполнения нового бинарного кода

Ничего, что мы компилируем код в рантайме и потом его запускаем? Если не веришь, что машкод, то вот тебе disassemble

CL-USER> (disassemble '*f*)

; disassembly for (LAMBDA (X))
; 0ABEDF2C:       F6C303           TEST BL, 3                 ; no-arg-parsing entry point
;       2F:       740F             JEQ L0
;       31:       8BC3             MOV EAX, EBX
;       33:       2407             AND AL, 7
;       35:       3C07             CMP AL, 7
;       37:       751F             JNE L2
;       39:       8A43F9           MOV AL, [EBX-7]
;       3C:       3C22             CMP AL, 34
;       3E:       7718             JNBE L2
;       40: L0:   BF08000000       MOV EDI, 8
;       45:       8BD3             MOV EDX, EBX
;       47:       E8042241F6       CALL #x1000150             ; GENERIC-+
;       4C:       7302             JNB L1
;       4E:       8BE3             MOV ESP, EBX
;       50: L1:   8B5DFC           MOV EBX, [EBP-4]
;       53:       8BE5             MOV ESP, EBP
;       55:       F8               CLC
;       56:       5D               POP EBP
;       57:       C3               RET
;       58: L2:   8B05F8DEBE0A     MOV EAX, [#xABEDEF8]       ; 'NUMBER
;       5E:       CC0A             BREAK 10                   ; error trap
;       60:       03               BYTE #X03
;       61:       1F               BYTE #X1F                  ; OBJECT-NOT-TYPE-ERROR
;       62:       CE               BYTE #XCE                  ; EBX
;       63:       0E               BYTE #X0E                  ; EAX
;       64:       CC0A             BREAK 10                   ; error trap
;       66:       02               BYTE #X02
;       67:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       68:       4D               BYTE #X4D                  ; ECX
NIL

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

Reset

Загрузить и исполнить новый бинарный код в процессе работы программы. Мы по-моему уже страниц 5 об этом говорим.

Ну тогда так:

;; some-code.lisp
(defun foo (x) (+ x 10))
;; bar.lisp
(defun foo ()
  11)

(defun bar ()
  (print (foo))
  (load (compile-file "foo.lisp"))
  (print (foo 10)))
;;REPL
(bar)

11 
; compiling file "/home/dvk/tmp/foo.lisp" (written 05 FEB 2010 11:07:20 AM):
; compiling (DEFUN FOO ...)

; /home/dvk/tmp/foo.fasl written
; compilation finished in 0:00:00.002
STYLE-WARNING: redefining FOO in DEFUN

20 

Это с загрузкой кода.

Можно же просто код компилировать:

(defun baz ()
  (let ((f (compile nil `(lambda (x) (+ x ,(random 10))))))
    (print (funcall f 10))
    (print (funcall f 11))
    (print (funcall f 12))))

(baz)
;12 
;13 
;14 
(baz)
;15 
;16 
;17 

В этом примере в рантайме формируется код функции, он же в рантайме компилируется (SBCL, CCL, Allegro и др. скомпилируют в машинный код), и потом вызывается.

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

>> Где генерация в рантайме маш. кода, заменяющего имеющийся?

Можно вызвать отдельным процессом gcc && objcopy, а потом засосать это в буфер mem.


т.е. по вашему вызов внешней программы - это не инородный инструмент? Да и как же так, недавно вы утверждали, что использовать сторонние более низкоуровневые средства, т.е. ОС, - это неполноценность, а сами в своём коде использовали mmap. Все пытаются жульничать и все это крайне неумело скрывают.

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

когда далеко не очевидно в набор каких ассемблерных инструкций развернется то или иное выражение

Неочевидно кому ?

YUG-BASE> (disassemble #'(lambda () (intersection '(1 2 3) '(3 4 5))))
; disassembly for (LAMBDA ())
; 045C6C04:       488B1595FFFFFF   MOV RDX, [RIP-107]         ; '(1 2 3)
                                                              ; no-arg-parsing entry point
;       0B:       488B3D96FFFFFF   MOV RDI, [RIP-106]         ; '(3 4 5)
;       12:       488B0597FFFFFF   MOV RAX, [RIP-105]         ; #<
                                                              ;   FDEFINITION object for INTERSECTION>
;       19:       B910000000       MOV ECX, 16
;       1E:       FF7508           PUSH QWORD PTR [RBP+8]
;       21:       FF6009           JMP QWORD PTR [RAX+9]
;       24:       CC0A             BREAK 10                   ; error trap
;       26:       02               BYTE #X02
;       27:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       28:       54               BYTE #X54                  ; RCX
NIL

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

Что стоит за этими самыми compile, load и т.п. ? Где простейшая низкоуровневая конструкция типа такой

(setq f '\55\48\89\e5\89\7d\ec\8b\45\ec\83\c0\02\89\45\fc\8b\45\fc\c9\c3')
(print (funcall f 1)) ; печатаем 3

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

Что стоит за этими самыми compile, load и т.п. ? Где простейшая низкоуровневая конструкция типа такой

compile - компиляция лиспового кода и возврат функции

compile-file - компиляция файла с лисповым кодом

load - загрузка скомпилированного лиспового файла

Т.е., вам нужно вызвать функцию с сишным соглашением о вызове, находящуюся в памяти? Ну тогда так:

(foreign-funcall (foreign-symbol-pointer "strlen") :string "foo" :int)
=> 6
Вместо (foreign-symbol-pointer «strlen») нужно подставить указатель на начало такой функции.

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

> т.е. по вашему вызов внешней программы - это не инородный инструмент?

Есть принципиальная возможность сделать эти «инородные инструменты» в виде библиотек и подлинковать к своей программе. Тут важно, что весь процесс полностью понятен и прозрачен.

Что происходит в лиспе похоже на какую-то темную магию и никто уже на протяжении нескольких страниц не может ответить на вопрос как же лисп загружает новый бинарник в память ? Если не можете сами объяснить, хоть ссылку на литературу дайте.

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

Так, значит compile компилирует код и помещает бинарник куда-то в память, а потом с помощью foreign-symbol-pointer его дергает по адресу? Хорошо, а как в лиспе адреса узнаются?

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

>а протяжении нескольких страниц не может ответить на вопрос как же лисп загружает новый бинарник в память ?

Ну так спрашивать надо, а не стесняться. Процесс загрузки включает в себя выполнение кода, производящего побочные эффекты, приводящие к включению кода в текущий образ. Например, если в файле был (defun foo ...) (определение функции), то при загрузке исполнится код (setf (symbol-function 'foo) ...). И т.п. Но это один из вариантов загрузки, зависит от имплементации.

А вам зачем это знать? Достаточно того, что работает. В лиспе работа с кодом отличается от работы с кодом в Си, не надо ожидать, что все будет одинаковым.

Что происходит в лиспе похоже на какую-то темную магию

Не похоже.

Так, значит compile компилирует код и помещает бинарник куда-то в память, а потом с помощью foreign-symbol-pointer его дергает по адресу?

Нет, foreign-funcall дергает только внешний, инородный код. Как дергает код лисповый рантайм, зависит от имплементации. Например, в SBCL объект типа FUNCTION является указателем (точнее, указатель + тэг) на исполнимый код, и FUNCALL осуществляет проверку тэга, помещает параметры согласно соглашению о вызове, и делает джамп по указателю.

(foreign-funcall «strlen» :string «foo» :int) => 3

Хм, да, точно, спасибо.

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

в лиспе функция это объект ключ -> значение
ключ это имя
значение это код

когда мы пишем

(setf my-fun (compile nil (lambda () "hello")))
мы получаем объект с именем my-fun и кодом который вернет комплиляция этой лямбды

сишные функции так же оборачиваются:

(defcfun "strlen" :int
    "Calculate the length of a string."
    (n :string))

будет создана лисповская функция в коде которй будет вызвана сишная функция по адресу

таким образом нам не нужны адреса функций в клиентском коде - только имена

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

> Имхо страшноватое, в связи с использованием #_ reader macro, но юзабельное. Я, впрочем, пока его ещё не очень сильно использовать пытался, так, посмотрел немного некоторое время назад и отправил автору (David Lichteblau) патч для поддержки интеграции со SLIME REPL — http://repo.or.cz/w/commonqt.git/commitdiff/ca95fa435cdc0ff4afe7aa761d2de03f0...

В cl-smoke есть готовая интеграция с REPL и нет #_ :-) Правда я до «попробовать» еще не добрался - ему нужен SMOKE из SVN, который требует библиотеки из KDE4.4, так что жду релиза KDE.

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

> Есть принципиальная возможность сделать

Но нет реального удобного и полезного инструмента, почему?

Тут важно, что весь процесс полностью понятен и прозрачен.


Но, судя по отсутствии реальных инструментов, почти не реализуем?

Кстати, вы на протяжении уже 3 страниц игнорируете ссылку - http://common-lisp.net/project/movitz/movitz.html. Можете как-то всё таки прокоментируете?

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

> Но нет реального удобного и полезного инструмента, почему? Но, судя по отсутствии реальных инструментов, почти не реализуем? http://common-lisp.net/project/movitz/movitz.html. Можете как-то всё таки прокоментируете?

Это всего лишь показ принципиальной возможности работы лисп-машины в качестве ядра ОС. Это можно прокомментировать вашими же двумя предложениями, написанными выше.

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

Любите работать с лохматым кодом?

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

По поводу динамической генерации кода. Простой пример.

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace Test
{
    class Program
    {
        static object CreateObject(string message)
        {
            var asmName = new AssemblyName(Guid.NewGuid().ToString());
            AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
                asmName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assembly.DefineDynamicModule(asmName.Name);

            TypeBuilder typeBuilder = moduleBuilder.DefineType("Class" + Guid.NewGuid(),
                TypeAttributes.Class | TypeAttributes.Public, typeof(object));

            MethodBuilder methodBuilder = typeBuilder.DefineMethod("PrintMessage", MethodAttributes.Public);
            ILGenerator methodIlGen = methodBuilder.GetILGenerator();

            methodIlGen.Emit(OpCodes.Ldstr, message);
            MethodInfo writeLineInfo = typeof(Console).GetMethod("WriteLine", new[] { typeof(string) });
            methodIlGen.Emit(OpCodes.Call, writeLineInfo);
            methodIlGen.Emit(OpCodes.Ret);

            Type type = typeBuilder.CreateType();
            return Activator.CreateInstance(type, new object[] { });
        }

        static void Main(string[] args)
        {
            dynamic obj = CreateObject("Hello");
            obj.PrintMessage();

            obj = CreateObject("World");
            obj.PrintMessage();

            Console.ReadKey();
        }
    }
}

Сдесь вызовом метода CreateObject мы динамически создаём новый класс (новый тип), в котором создаём метод, в котором создаём код, выводящий нужную нам строку.

NightmareZ
()

Ну так кнопки когда уже заработают?!

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

классно.
осталось только все это сохранить
для потомков

в лиспе можно вынести генерацию кода
в comile time. тогда при следующей загрузке
весь код будет доступен.

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

> Сдесь вызовом метода CreateObject мы динамически создаём новый класс (новый тип), в котором создаём метод, в котором создаём код, выводящий нужную нам строку.

Покажи байт-код скомпилированного класса, тогда будет то.

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

По поводу динамической генерации кода.

Да вы смелый и видимо очень трудолюбивый человек! Вы просто представьте, сколько усилий потребуется для генерация какого-либо полезного кода. CL версия:

(defmacro defhello (str)
  `(defun ,(intern (format nil "HELLO-~:@(~A~)" str)) ()
     (print ,str)))

(defhello "NightmareZ")

(hello-nightmarez)

Тривиальнейшая в CL задача превращается в C# в нечто мозгодробительное (интересно, а как это вообще отлаживать?), отличный пример на тему того, что «настоящие мужчины сидят под windows» (с) :)

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

> Тривиальнейшая в CL задача превращается в C# в нечто мозгодробительное

И что ты там нашел мозгодробительного? Многословно, да. Но в его примере и делается гораздо больше.

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

> И что ты там нашел мозгодробительного? Многословно, да.

Но в его примере и делается гораздо больше.


Хорошо, сможете привести пример генерации какого либо полезного кода на C# и рассказать о стратегии отладки этого? Написали пару сотен строк, запустили, работает неправильно, что будем делать?

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

После вызова CreateObject(«Hello») генерируется

.method public hidebysig instance void PrintMessage() cil managed
{
    .maxstack 8
    L_0000: nop 
    L_0001: ldstr "Hello"
    L_0006: call void [mscorlib]System.Console::WriteLine(string)
    L_000b: nop 
    L_000c: ret 
}

После вызова CreateObject(«World») генерируется

.method public hidebysig instance void PrintMessage() cil managed
{
    .maxstack 8
    L_0000: nop 
    L_0001: ldstr "World"
    L_0006: call void [mscorlib]System.Console::WriteLine(string)
    L_000b: nop 
    L_000c: ret 
}

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

По поводу динамической генерации кода. Простой пример.

Ну мы все поняли:) Такой ассемблер, да еще и объектно-ориентированный. Т.е., принципиальная возможность есть, но на практике нереально.

За кадром осталось:

1) Скорость такой генерации

2) Несобираемость сборок сборщиком мусора да и вообще их несобираемость. Т.е., сгенерили сборку с классом и методом. И они так и будут висеть в памяти, пока не прибьется аппдомен.

В 4м сишарпе/дотнете положение немного улучшено (уау, у них есть Parse Trees! и у них есть метод Compile! уау!!).

Но в его примере и делается гораздо больше.

Гораздо больше?

1) А можно сделать так, чтобы не надо было делать гораздо больше?

2) Расширим лисповый пример. Т.е., то же самое, что и

(defgeneric print-yourself (thing))

(defun create-object (message)
  (let ((class-name (gensym)))
    (eval `(progn (defclass ,class-name () ())
                  (defmethod print-yourself ((thing ,class-name))
                     (format t "~A" ,message))))))

(print-yourself (create-object "Hello"))
(print-yourself (create-object "World"))
dmitry_vk ★★★
()
Ответ на: комментарий от archimag

Недавно встала передо мной такая задача.

Есть некоторые объекты некоторых типов. У этих объектов есть свойства. Некоторым свойствам задан атрибут [A].

Нужно в рантайме создавать прокси-объекты, которые будут иметь одноимённые свойства вышеназванных объектов, но лишь те, у которых задан атрибут [A]. Обращения к этим свойствам прокси-объектов должны переадресовываться. Ну т.е.:

class MyAttrib : Attribute { }

class MyClass
{
    public A { get; set; }
    public B { get; set; }

    [MyAttrib]
    public C { get; set; }
    
    public D { get; set; }
}

MyClass obj = new MyClass();

Для объекта obj в рантайме должен быть создан прокси-объект соответствующий следующему классу:

class MyProxy
{
    private MyClass obj;
    public MyProxy(MyClass obj) { this.obj = obj; }

    public C
    {
        get { return obj.C; }
        set { obj.C = value; }
    }
}

Решил я её таким способом:

public object CreateProxy()
{
    // Создаём сборку.
    var asmName = new AssemblyName("GlyphProxyAssembly.dll");
    AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
        asmName, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder = assembly.DefineDynamicModule(asmName.Name);

    // Прокси-класс.
    TypeBuilder typeBuilder = moduleBuilder.DefineType("GlyphProxy" + Guid.NewGuid(), TypeAttributes.Class | TypeAttributes.Public, typeof(object));
    
    // Приватное поле, содержащее ссылку на объект, с которым связан данный прокси.
    FieldBuilder ownerField = typeBuilder.DefineField("_owner", GetType(), FieldAttributes.Private);

    // Ищем все поля в данном объекте, помеченные атрибутом ActivePropertyAttribute
    // и создаём соответствующие поля в прокси объекте.
    foreach (var prop in GetType().GetProperties())
        foreach (var attrib in prop.GetCustomAttributes(true))
            if (attrib is ActivePropertyAttribute)
            {
                // Свойство.
                PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(prop.Name,
                    PropertyAttributes.HasDefault,
                    prop.PropertyType,
                    null);

                // Геттер.
                MethodBuilder methodGet = typeBuilder.DefineMethod("Get_" + prop.Name,
                    MethodAttributes.Public,
                    prop.PropertyType,
                    new Type[] { });

                // Код геттера: берём значение свойства из связанного объекта.
                ILGenerator ilGet = methodGet.GetILGenerator();

                ilGet.Emit(OpCodes.Ldarg_0);
                ilGet.Emit(OpCodes.Ldfld, ownerField);
                ilGet.EmitCall(OpCodes.Callvirt, prop.GetGetMethod(), null);
                ilGet.Emit(OpCodes.Ret);

                // Сеттер.
                MethodBuilder methodSet = typeBuilder.DefineMethod("Set_" + prop.Name,
                    MethodAttributes.Public,
                    null,
                    new[] { prop.PropertyType });

                // Код сеттера: сохраняем значение свойства в связанный объект.
                ILGenerator ilSet = methodSet.GetILGenerator();

                ilSet.Emit(OpCodes.Ldarg_0);
                ilSet.Emit(OpCodes.Ldfld, ownerField);
                ilSet.Emit(OpCodes.Ldarg_1);
                ilSet.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), new[] { prop.PropertyType });
                ilSet.Emit(OpCodes.Ret);

                // Назначем геттер и сеттер свойству.
                propertyBuilder.SetGetMethod(methodGet);
                propertyBuilder.SetSetMethod(methodSet);

                break;
            }

    // Конструктор прокси-класса.
    var ctorParams = new[] { GetType() };
    ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, ctorParams);
    ILGenerator ilgen = constructorBuilder.GetILGenerator();

    // Вызываем конструктор родителя.
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

    // Загружаем ссылку на объект, с которым связан данный прокси в приватное поле.
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Stfld, ownerField);

    ilgen.Emit(OpCodes.Ret);
            
    // Создаём и возвращаем тип прокси-объекта.
    Type type = typeBuilder.CreateType();
    return Activator.CreateInstance(type, new object[] { this });
}

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

>Хорошо, сможете привести пример генерации какого либо полезного кода на C# и рассказать о стратегии отладки этого?

Я вот знаю, что NHibernate (дотнетовский ORM) таким вот образом генерирует код для объектов-оберток (а все из-за того, что в .NET нет метаобъектного протокола для объектной системы). Правда, для этого используется ряд оберток над кодогенерацией. И тормозит все это жутко.

Как они это отлаживают, понятия не имею.

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

> Хорошо, сможете привести пример генерации какого либо полезного кода на C#

Я - нет. С C# вообще не сталкивался.

рассказать о стратегии отладки этого?

Думаю, отладчик на уровне IL. И прежде чем ты скажешь «низкий уровень!!!!11», заметь, что тебе такое недоступно (в Лиспе нет стандартизированного IL). Есть ли в стандартной библиотеке C# компилятор - ХЗ, но к сравнению языков это не относится.

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

>Думаю, отладчик на уровне IL. И прежде чем ты скажешь «низкий уровень!!!!11», заметь, что тебе такое недоступно (в Лиспе нет стандартизированного IL).

В лиспе есть макроэкспанд, которым макросы отлаживаются на ура. И есть обычный отладчик, который отлаживает как код макросов, так и их раскрытие.

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

Есть ли в стандартной библиотеке C# компилятор - ХЗ

Есть. Можно в рантайме компилировать код на шарпе и сразу использовать результат.

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

> В лиспе есть макроэкспанд

Спасибо, Капитан. Но вы не заметили, что я говорил о другом.

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

> Можно в рантайме компилировать код на шарпе и сразу использовать результат.

Видимо, это и хотели увидеть наши друзья-лисперы.

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

Видимо, это и хотели увидеть наши друзья-лисперы.

Ну можно и так. Я как-то делал плагины на JScript, которые подгружались, компилировались и запускались. И, да, всё искаропки.

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

> Думаю, отладчик на уровне IL

заметь, что тебе такое недоступно


Э.. а зачем мне такое вообще могло бы потребоваться?

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

> Недавно встала передо мной такая задача.

...

Решил я её таким способом:



И вы таки до сих не видите преимуществ Common Lisp? Ваше мужество и трудолюбие вызывают у меня искренне восхищение. Всё так «настоящие мужчины сидят под windows», по любому.

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

И вы таки до сих не видите преимуществ Common Lisp? Ваше мужество и трудолюбие вызывают у меня искренне восхищение. Всё так «настоящие мужчины сидят под windows», по любому.

Я, в отличие от некоторых присутствующих в данной теме людей, не говорил, что *language_name* крут, а лисп - отстой. Я лишь привожу примеры, как я делаю на шарпе. Лиспа я не знаю, потому не могу объективно что-то против него или за него сказать. Но могу сказать против Си, например, то, что во многих задачах Си уныл чуть болеее, чем полностью и приведённый выше код подмены функции - суть сношательство мозга.

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

>> Думаю, отладчик на уровне IL

заметь, что тебе такое недоступно

а зачем мне такое вообще могло бы потребоваться?

Написать компилятор.

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

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

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

> Я лишь привожу примеры, как я делаю на шарпе. Лиспа я не знаю, потому не могу объективно что-то против него или за него сказать.

В твоем примере шарповский код генерит низкоуровневый байт-код. Это как бы совсем другое. Есть, конечно, CodeDom, но до лиспа все равно дотнету далеко в этом плане.

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

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

Кодогенерация вообще мало где нужна ;)

А вышеприведённый пример с прокси-объектами был вынужденой мерой для работы с компонентом, который работает не совсем корректно. Можно, безусловно, компонент написать свой, но это займёт в 100 раз больше времени.

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