Является ли C# код кодом на языке Lisp?
хотя ты, чудо, меня игноришь
"С СУЩЕСТВЕННЫМИ ограничениями по синтаксису"собственно в интернетах так и пишут
Lisp is a form of syntax (and a few primitives like Eval and quote) that makes it easy to write programs that manipulate programs.
т.е. Lisp мощный инструмент, но ограничен определенной синтаксической формой
это не противоречит написанному на вики, имхо
Любая достаточно сложная программа на C или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp[1]
)
— '(Филип Гринспен
Собственно, я этим интересуюсь вот в каком контексте. Для работы с СУБД нужно фактически программой генерировать программу. Поскольку везде пишут, что Lisp хорош для задачи генерации программы. То почему в качестве интерфейса к СУБД не сделали Lisp? Мое имхо такое, что все же специализированный синтаксис (SQL) для людей более понятен, читаем и т.п. Я не прав? Вот тут даже lisper-ы сами говорят, что, по крайней мере, "Undoubtedly, select is harder to learn to use than query. (ref1)"
Если что, я лисп не знаю, только слышал Я понимаю так, что можно расширять синтаксис и для этого предоставлены большие возможности, но почему отсюда должно сделовать, что можно любой язык заэмулировать, я не понимаю.
Я понимаю так, что можно расширять синтаксис и для этого предоставлены большие возможности, но почему отсюда должно сделовать, что можно любой язык заэмулировать, я не понимаю.одно дело большие возможности, другое "без ограничений". Если без ограничений, то отсюда следует, что можно эмулировать любой язык.
Если что, я лисп не знаю, только слышаля тоже. Но насколько я понял, там все расширения остаются в рамках "списочной записи" — элементы списка в скобочках через пробел.
но почему отсюда должно сделовать, что можно любой язык заэмулировать, я не понимаю.ПОТОМУ ЧТО ТЫ ЕЩЕ НЕ ПРОНИКСЯ СУЩНОСТЬЮ CONTROLLABLE QUERY!11!11
попробуй объяснить
Мое имхо такое, что все же специализированный синтаксис (SQL) для людей более понятен, читаем и т.п. Я не прав? Вот тут даже lisper-ы сами говорят, что, по крайней мере, "Undoubtedly, select is harder to learn to use than query. (ref1)"Следующая же фраза же: "once you know your way about SQL this is a comparatively small step to take - the real unpleasantness lies in the SQL;".
Большинство привыкло к инфиксной записи и к тому, что скобки можно ставить где и как угодно.
Поэтому лисп малопопулярен.
То есть, можно вполне написать на лиспе макросы, которые сделают корректными записи вроде:
(c-sharp-def '(GenericList < string > list2 = new GenericList < string >
(c-sharp 'Console :point WriteLine ("Hello world!" (Console :point WriteLine("Farewell world!"
(необходимость пробелов между шарповыми токенами зависит от того, сколько трудолюбия ты в этот макросы вложил).
Но проще, наверное, обратиться к FFI. А то тут будет, в общем-то, интерпретация почти полная (только без токенизации текста тоже не сахар.
И да, лисп - это не совсем язык, лисп - это семейство диалектов. Не во всех из них функции являются первоклассными, в некоторых из них есть классы и объекты, не везде можно поймать продолжение, etc.
т.е. Lisp мощный инструмент, но ограничен определенной синтаксической формойВ нем есть Read Macros, которые позволяют парсить произвольный текст, не только sexp'ы.
Not only does lisp provide direct access to code that has been parsed into a cons cell structure, but it also provides access to the characters that make up your programs before they even reach that stage. Although regular macros work on programs in the form of trees, a special type of macro, called a read macro, operates on the raw characters that make up your program.http://letoverlambda.com/index.cl/guest/chap4.html
Так что при большом желании да, можно C# сделать частью лиспа. В немерле, другом макросоориентированном языке, так и сделали примерно - запилили компилятор шарпа немерловыми средствами.
необходимость пробелов между шарповыми токенами зависит от того, сколько трудолюбия ты в этот макросы вложилчто ты этим хочешь сказать? Ты про парсинг текста? и потерю основного приемущества Lisp записи:
But Lisp is different. Lisp macros do have access to the parser, and it is a really simple parser. A Lisp macro is not handed a string, but a preparsed piece of source code in the form of a list, because the source of a Lisp program is not a string; it is a list. And Lisp programs are really good at taking apart lists and putting them back together. They do this reliably, every day.
В нем есть Read Macros, которые позволяют парсить произвольный текст, не только sexp'ы.А Read Macros позволяет вот это "the syntax allows a natural and powerful intermixing of lisp forms and SQL. (ref1)"?
Так что при большом желании да, можно C# сделать частью лиспа. В немерле, другом макросоориентированном языке, так и сделали примерно - запилили компилятор шарпа немерловыми средствами.хочется чтобы это сочеталось со стандартными средствами декомпозиции основного языка, например, должны работать приемы типа extract method для выделения фрагмента кода (с последующим его вызовом в нескольких местах). Иначе это просто занимательная игрушка.
Какие приемы реализуешь, такие и будут работать. Тьюринг полнота в компайл тайме и доступность компилятора в рантайме позволяют реализовать любые фантазии. Вопрос лишь в цене и целесообразности.
реализуешьт.е. готового ничего не получаешь? Тогда, что остается от lisp-а?
реализуешьсейчас интересна не реализация, а спецификация. Со списочной записью всё понятно, там работают операции над списками. А когда отходим от списочной записи, то чем это отличается от спецификации своего языка с нуля?
Ну и понятно, что чаще всего read macros генерят уже списки/деревья в привычном для лиспа виде, а с ними уже работать можно всем богатством лиспового арсенала.
Кстати, по приведенной мной выше ссылке (советую ознакомиться) вопрос исходного поста практически цитируется:
While the transformations of code done by regular macros are only used for turning lisp code into new lisp code, read macros can be created so as to turn non-lisp code into lisp code. Like regular macros, read macros are implemented with functions underneath so we have available the full power of the lisp environment. Like macros that increase productivity because they create more concise domain specific languages for the programmer to use, read macros boost productivity by allowing expressions to be abbreviated to the point where they aren't even lisp expressions anymore. Or are they?
If all we have to do to parse these non-lisp domain specific languages is write a short read macro, maybe these non-lisp languages really are lisp, just in a clever disguise. If XML can be directly read in by the lisp reader[XML-AS-READ-MACRO], maybe XML, in a twisted sort of sense, is actually lisp. Similarly, read macros can be used to read regular expressions and SQL queries directly into lisp, so maybe these languages really are lisp too. This fuzzy distinction between code and data, lisp and non-lisp, is the source of many interesting philosophical issues that have perplexed lisp programmers since the very beginning.
а например добавить новый вид строковых литералов, добавить LINQ или SQL выражения и использовать их посреди лиспового кода.строковые литералы ... посреди кода... Это не полные условия цели (LINQ этим и плох). Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных. Т.е. хочется иметь лисповый дуализм кода и данных, но не только для спискового синтаксиса, но и для синтаксиса SQL и т.д.
ну и со статической типизацией, да
Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных.nemerle же все это делает - чем он тебя не устраивает?
nemerle же все это делает - чем он тебя не устраивает?в рантайме с SQL-ем? я такого не видел, запости ссылку.
хочется того, что сейчас делают многие ORM где используется Query Object или Query Monad, но с человеческим синтаксисом. LINQ немного приблизил человеческий синтаксис, но потеряли работу со структурой данных в рантайме.
в рантайме с SQL-ем? я такого не видел, запости ссылку.видел пример про встраивание синтаксиса peg-а (встройку синтаксиса sql не видел)
[PegGrammar(Options = EmitDebugSources, start,
grammar
{
any = ['\u0000'..'\uFFFF'];
digit = ['0'..'9']+;
spaces : void = ' '*;
num : int = digit spaces;
unaryMinus : int = '-' spaces simplExpr;
...
}
})]
public class CalcParser
{
private num(digit : NToken) : int
{
int.Parse(GetText(digit
}
private unaryMinus(_minus : NToken, se : int) : int
{
-se
}
...
}
http://www.rsdn.ru/article/nemerle/PegGrammar.xml
покажи пример использования
в предыдущем посте внутри блока grammar используется не C#-синтаксис - это пример встройки своего синтаксиса
Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных.Именно так и будет. В процессе парсинга исходника компилятор, встретив LINQ выражение, вызывает твой read macro, тот генерит обычные деревья, они становятся частью общей структуры программы. Когда другие макросы потом что-то делают с этим кодом, они работают с привычными структурами, не подозревая, что часть этих списков была получена не чтением скобочек, а разбором LINQ выражения.
Если вдруг нужно после преобразования опять получить LINQ, на то есть pretty-printer'ы.
т.е. готового ничего не получаешь? Тогда, что остается от lisp-а?Ты можешь определить макрос, допускающий выражения на псевдокоде:
(define-syntax pseudocode-function ...)
Затем ты его используешь:
(pseudocode-function '(Int Increase(Int x) (return x + 1'
Так вот, после экспансии макроса это не будет уже код на псевдокоде. Экспансия макроса превратит его в код на лиспе. Если имплементация компилируемая, это произойдет при компиляции. Функцию эту ты потом можешь использовать как любую другую лисповую:
> (begin (display (pseudocode-Increase 10 (newline
11
Ну то есть см. пост а над моим.
ну и со статической типизацией, даТогда не лисп.
Именно так и будет. В процессе парсинга исходника компилятор, встретив LINQ выражение, вызывает твой read macro, тот генерит обычные деревья, они становятся частью общей структуры программы. Когда другие макросы потом что-то делают с этим кодом, они работают с привычными структурами, не подозревая, что часть этих списков была получена не чтением скобочек, а разбором LINQ выражения.Моя фраза “работа с выражениями как со структурой данных” требует более детальных пояснений. Рассмотрим три варианта записи выражения “a + b + c”:
Если вдруг нужно после преобразования опять получить LINQ, на то есть pretty-printer'ы.
Func<int> M1(int a, int b, int c) //Code1
{
return => a + b + c;
}
Expression<Func<int>> M2(int a, int b, int c) //Code2
{
return => a + b + c;
}
Func<Value<int>> M3(Value<int> a, Value<int> b, Value<int> c) //Code3
{
return => a + b + c;
}
Для Code2 и Code3 в рантайме имеем структуры данных. Сделаем для всех трех вариантов extract method.
Func<int> M1(int a, int b, int c) //Code1
{
return => a + N1(b, c);
}
int N1(int b, int c)
{
return b + c;
}
Expression<Func<int>> M2(int a, int b, int c) //Code2
{
return => a + N2(b, c);
}
int N2(int b, int c)
{
return b + c;
}
Func<Value<int>> M3(Value<int> a, Value<int> b, Value<int> c) //Code3
{
return => a + N3(b, c);
}
Value<int> N3(Value<int> b, Value<int> c)
{
return b + c;
}
Для Code1 и Code3 по сути ничего не поменялось. А для Code2 произошла катастрофа – мы не можем пройти по “b + c” как по структуре данных.
Таким образом, плюсы и минусы по отношению к идеальному решению:
Code2
1.1 (+) имеем структуру данных в рантайме;
1.2 (+) можно расширять на более сложные выражения (join, were, select и т.д. при этом получается хорошо читаемый синтаксис;
1.3 (-) плохое смешивание с обычным кодом, extract method и т.д.
Code3
2.1 (+) имеем структуру данных в рантайме;
2.2 (-) плохо расширяется на более сложные выражения;
2.3 (+) естественным образом смешивается с обычным кодом, extract method и т.д.
Теперь про Lisp. В случае обычного lisp-кода (списочная запись) нет различий между Code1 и Code3. Всегда можем получить структуру данных с помощью quoting. Это базовая особенность языка. Если lisp-код генерируется с помощью lisp-кода, то всё хорошо, поскольку упомянутых в пункте 2.2. “сложных выражений” просто нет. Однако когда встраиваем нелисповый синтаксис, то получаем недостатки, указанные в пункте 1.3. Поэтому вот тут и пишут, давайте вернемся к лисповому синтаксису, типа это “a comparatively small step”, зато получаем такую большую вкусную плюшку как “a natural and powerful intermixing of lisp forms and SQL”. Ну а small или big step это вопрос…
Func<string> M4 //Code4
{
return => "a + b + c";
}
После extract method:
Func<string> M4 //Code4
{
return => "a + " + N4;
}
string N4
{
return "b + c";
}
Плюсы и минусы по отношению к идеальному решению:
Code4
3.1 (+) имеем структуру данных в рантайме, поскольку текст можно всегда распарсить;
3.2 (+) можно расширять на более сложные выражения, здесь вообще без ограничений;
3.3 (+) естественным образом смешивается с обычным кодом, extract method и т.д.
3.4 (-) требуется специальный механизм передачи параметров из основного кода и обратно (в примере a, b, c);
3.5 (-) подверженность глупым ошибкам при написании, сложность при навигации по коду, сложность при внесении изменений.
-Можно...
-Но тогда будет неудобно ковырять в носу!
-Ну, да.
Ты чего хотел-то?
Вот и спрашивает, можно ли в функциональных языках так, чтобы без функциональных языков.
Ты чего хотел-то?Чтобы решение не содержало (- см. выше. Другими словами, SQL синтаксис внутри языка с возможностью extract method любого фрагмента SQL выражения. Но без побочных минусов как в Code4. Еще хорошо, если проверка синтаксиса на этапе компиляции. Ни в одном современном языке это не наблюдается C#, Java, Scala, Haskell, Lisp, Ruby, Python.
Подозреваю, что Шуреск хочет, чтобы как в функциональных языках, но чтобы без функциональных языков.причем тут именно функциональность не понятно, тут всё и Query Object, и монады, и "With LINQ's expression trees, they seem to tackle the same problem as Lisp does with S-expressions (ref1)". Но ни что из этого не обходится без минусов, как я описал выше.
Вот и спрашивает, можно ли в функциональных языках так, чтобы без функциональных языков.
Ты чего хотел-то?Например, я хочу вот так смешивать (миксовать) основной язык и SQL. Пусть у нас в коде вот такой запрос:
SELECT A1,
B2
FROM A
LEFT JOIN B
ON A.A2 = B.A2
WHERE A1 = 'test1'
AND EXISTS (
SELECT 1
FROM C
WHERE C1 = 'test2'
AND A.A3 = C.A3
)
Я хочу вот так сделать extract method для M1 и M2:
SELECT A1,
B2
FROM A
M1
WHERE A1 = 'test1'
M2
? M1(?)
{
return LEFT JOIN B
ON A.A2 = B.A2;
}
? M2(?)
{
return AND EXISTS (
SELECT 1
FROM C
WHERE C1 = 'test2'
AND A.A3 = C.A3
);
}
А для Code2 произошла катастрофа – мы не можем пройти по “b + c” как по структуре данных.какое обоснование этого тезиса? только то, что конкретная библиотека не умеет ходить по коду методов?
[...]
1.3 (-) плохое смешивание с обычным кодом, extract method и т.д.
какое обоснование этого тезиса? только то, что конкретная библиотека не умеет ходить по коду методов?на первый взгляд вроде можно и по IL ходить, но Expression<T> не просто так ввели, наверное. Тогда бы и IEnumerable вместо IQueryable можно было бы использовать. Думаю, по IL задолбаешься ходить, его для этого не делали, это хак. .Net не Lisp же .
есть roslyn, который успешно ходит по с#
ну во-первых надо в рантайме, а там C#-а уже нет, там IL
ну а ты как думаешь, зачем они ввели Expression<T> и специальную обработку компилятором для него?
ну во-первых надо в рантайме, а там C#-а уже нет, там ILвообще, reflector же осиливает преобразование IL -> C#, и вполне адекватно. Поэтому я не вижу в чем проблема?..
ну а ты как думаешь, зачем они ввели Expression<T> и специальную обработку компилятором для него?потому что хотели быстро решить частную задачу в виде запиливания sql-linq
частную задачу в виде запиливания linqчто за задача? формулировка слишком расплывчета.
sql-linqтак понятнее
что за задача?поправился: sql-linq.
Им было лень делать полноценный анализатор IL-а в виде высокоуровневого преобразователя (хотя reflector показывает что это возможно и вместо этого быстро вкосячили костыль в виде Expression
какое обоснование этого тезиса? только то, что конкретная библиотека не умеет ходить по коду методов?добавим еще немного логики
Expression<Func<int>> M2(int a, int b, int c) //Code2
{
return => a + N2(b, c);
}
int N2(int b, int c)
{
return DateTime.Now.Day > 13 ? b + c : b;
}
Func<Value<int>> M1(Value<int> a, Value<int> b, Value<int> c) //Code3
{
return => a + N3(b, c);
}
Value<int> N3(Value<int> b, Value<int> c)
{
return DateTime.Now.Day > 13 ? b + c : b;
}
В Code3 всё понятно мы получаем в рантайме структуру данных либо "a+b+c", либо "a+b". А ты собираешься в рантайме анализировать тело метода N2 и по хитрому алгоритму его частично выполнять в CLR-е, а частично отправлять на SQL-сервер?
по хитрому алгоритму его частично выполнять в CLR-евесь хитрый алгоритм состоит из осознания, что есть структура вида: tree<expression>, и что для каждого expression либо есть аналог в sql, и тогда его можно перевести в sql и там выполнить, либо аналога нет, и его необходимо выполнять на уровне кода.
весь хитрый алгоритм состоит из осознания, что есть структура вида: tree<expression>, и что для каждого expression либо есть аналог в sql, и тогда его можно перевести в sql и там выполнить, либо аналога нет, и его необходимо выполнять на уровне кода.имхо, что выполнять, а что отправлять должен решать программист-пользователь. Иначе работа с СУБД будет неэффективной (что часто встречается даже в сегодняшней реализации linq). Всё выражение "DateTime.Now.Day > 13 ? b + c : b;" имеет аналог в sql, но условный оператор можно выполнить в CLR, Code3 так и делает, и из кода намеренья программиста четко просматриваются.
имхо, что выполнять, а что отправлять должен решать программист-пользователь.тогда это должно делаться через явную пометку (а не неявную): либо через аттрибуты, либо через использование спец-блока, а еще лучше через явное внесение табличной информации - какие конструкции выгодно выполнять локально, а какие в sql
http://www.cs.utexas.edu/~wcook/papers/SafeQuery05/SafeQuery...
Ты рискнешь это использовать в коммерческом проекте?
кстати, вот то, о чем ты говоришь Ты рискнешь это использовать в коммерческом проекте?
Когда MS объявит, что LINQ не оправдал ожиданий и вообще теперь вместо шарпа яваскрипт, то все это творчество придется выкинуть на помойку. В то же время, приложения, написанные в 80-е на plsql, выглядят сейчас абсолютно современно (и будут также выглядеть через 20 лет).
В то же время, приложения, написанные в 80-е на plsql, выглядят сейчас абсолютно современно (и будут также выглядеть через 20 лет).ты так говоришь, как будто это хорошо.
Скорее это обычно говорит о застое в определенной области.
Скорее это обычно говорит о застое в определенной области.А чего тебе не хватает в SQL и PL\SQL?
А чего тебе не хватает в SQL и PL\SQL?высокоуровневой параметризации - что обычно и пытаются достичь с помощью кучи разных способов. в том числе, с помощью sql-linq
высокоуровневой параметризации - что обычно и пытаются достичь с помощью кучи разных способов. в том числе, с помощью sql-linq
Делается вьюха, в которой выставляются все столбцы, по которым надо фильтровать. Корректность вьюхи проверяется в compile time сервером СУБД. После этого генерация sql сводится к добавлению условий вида
and ( column1 = :param1 )Таким образом, динамическая типизация осталась, но ее очень немного и на практике никаких проблем она не создаст. В конце концов, в том же шарпе вместо значения в функцию всегда может прийти null. Это тоже своего рода динамическая типизация. Но почему-то в данном случае никто не городит монструозные фреймворки для борьбы с ней.
После этого генерация sql сводится к добавлению условий видаэто только верхушка айсберга для высокоуровневой параметризации.
Но почему-то в данном случае никто не городит монструозные фреймворки для борьбы с ней.их сотни! Того же Липперта который год просят сделать краткий синтаксис для быстрой обработки null-я, но он пока упирается
это только верхушка айсберга для высокоуровневой параметризации.Можешь привести реалистичный пример, несводимый к тому что я написал?
даже разные виды сортировки к этому не сводятся
Можешь привести реалистичный пример, несводимый к тому что я написал?Из тех, что лежат в общем доступе:
Пример1
Пример2, см. Раздел 1
У нас на работе таких примеров много, это обычные будничные ситуации.
Таким образом, динамическая типизация осталась, но ее очень немного и на практике никаких проблем она не создаст.Сильно субъективное мнение. Если ты считаешь его верным, опубликуйся где-нибудь. Получишь фитбек, и поймешь что в реальности всё разнообразнее. Вот люди публикуются, и у них вот такое мнение:
Встраиваемый SQL обеспечивает статически типизированный подход к явному выполнению запросов. Как обсуждается в разд. 7, одним из существенных недостатков встраивания является отсутствие поддержки динамических запросов.
...
Мы полагаем, что отсутствие поддержки динамических запросов является основной причиной отказа от большинства форм встраиваемого SQL [27].
http://citforum.ru/database/articles/impedance_mismatch/
в функцию всегда может прийти nullэто косяк C#-а, исправлять его сейчас не будут, поскольку уже поздно. В хорошем коде null-ы стараются не использовать. В новом языке Kotlin это решается на уровне языка. Мы используем Option Type. В Scala, F# тоже используются Option Type.
Оставить комментарий
6yrop
В subjet-е “C#” можно заменить на любой другой язык.В wikipedia пишут:
Если "without limit", то пишем расширение Lisp-а, и код на C#-е становится кодом на Lisp-е, ведь так? А если не так, то wikipedia гонит, и фразу "almost without limit" надо понимать как "С СУЩЕСТВЕННЫМИ ограничениями по синтаксису"?