Скрепя сердце, с сожалением и горечью, но тем не менее, движимый рациональным подходом к делу, перевел код программы для своего стартапа с Common Lisp на Microsoft .NET, а именно на C#.
Причины довольно банальны и прозаичны.
Первые две, в принципе, связаны, и хотя одну из них можно было бы компенсировать другой, у CL проблемы с обеими:
- Недостаток инфраструктуры. Ни для кого ни секрет, что у CL довольно неслабые проблемы с библиотеками, начиная со стандартной, и особенно с библиотеками под Windows. В принципе, это всё дело решаемое, и я бы мог для своего проекта написать всё сам, но, тем не менее, прикинув в голове, сколько бы это заняло усилий, и сколько бы родилось велосипедов, я от этого отказался. У .NET с другой стороны, инфраструктура даже стандартной библиотеки(BCL) просто огромна, и покрывает не только минимум вроде, например, I/O или работы с кодировками, но и более сложные и абстрактные вещи(см. пункт 4), и я уже не говорю про платформы для графического интерфейса(WPF, WinForms). В принципе, не будь второй проблемы, эта конкретная была бы не помехой. А вторая проблема такая:
- Недостаток специалистов со знанием CL. Проблема тоже довольно известная, и, к сожалению, стоящая очень остро. В моем случае - из всех подключенных к делу людей, с Common Lisp до такого уровня, чтобы писать на нем "production quality" код, знаком только я. Будь специалистов больше, как я уже сказал, проблема с инфраструктурой не стояла бы так остро.
- Порт SBCL под Windows, похоже, заброшен. Последний коммит был 4 месяца назад :( https://github.com/akovalenko/sbcl-win32-threads
- Linq Expression Trees. В .NET 3.5 Microsoft ввели такую сумасшедшую штуку, которая рядовыми программистами практически не используется, но которая очень полезна и необходима таким заклиненным на всяких лиспах людям, как я. Linq Expression Trees это инструмент для кодогенерации в рантайме. Они позволяют составлять AST(абстрактное синтаксическое дерево) из "выражений", компилировать его и запускать. Фича эта для моего проекта крайне необходимая, так как один из основных компонентов программы - генератор и компилятор packrat-парсеров из PEG.
Вот небольшой пример, для сравнения с CL:
(let* ((code `(lambda (x y) (+ x y)))
(f (compile nil code)))
(print (funcall f 1 2)))
;; ==> 3
На C#, с использованием Linq Expression Trees будет так:
using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
var xParam = Expression.Parameter(typeof(int), "x");
var yParam = Expression.Parameter(typeof(int), "y");
var body = Expression.Add(xParam, yParam);
var code = Expression.Lambda(body ,xParam, yParam);
var f = code.Compile();
Console.WriteLine(f.DynamicInvoke(1, 2));
// ==> 3
}
}
- C# довольно неплохой язык, хоть и не CL. В частности, в C# 4.0(а это теперь основной инструмент разработки) существует множество интересных фич, которые приближают его к таким мощным инструментам, как мой любимый Common Lisp. Здесь я имею ввиду возможности самого языка - начиная с анонимных делегатов и лямбд и заканчивая "dynamic", который фактически вводит в C# простенький аналог мультиметодов CLOS. Кстати, у кого-нибудь, возможно, может возникнуть вопрос "почему не на Java и JVM?" - так вот в том числе и поэтому. .NET и C# объективно удобнее и мощнее JVM и Java.
Такая "смена рельс" тем не менее не означает отсутствие в программе встроенного лиспа, о котором я писал ранее. Я планирую реализовать его сначала как интерпретатор, а потом, смотря по ситуации и по материальным, моральным и т.п. ресурсам, возможно и как компилятор в MSIL, а потом возможно и как компилятор в native-код.