Является ли C# код кодом на языке Lisp?

6yrop

В subjet-е “C#” можно заменить на любой другой язык.
В wikipedia пишут:
The reliance on expressions gives the language great flexibility. Because Lisp functions are themselves written as lists, they can be processed exactly like data. This allows easy writing of programs which manipulate other programs (metaprogramming). Many Lisp dialects exploit this feature using macro systems, which enables extension of the language almost without limit.
http://en.wikipedia.org/wiki/Lisp_%28programming_language%29...
Если "without limit", то пишем расширение Lisp-а, и код на C#-е становится кодом на Lisp-е, ведь так? А если не так, то wikipedia гонит, и фразу "almost without limit" надо понимать как "С СУЩЕСТВЕННЫМИ ограничениями по синтаксису"?

margadon

вероятно, С#-код становится исходными данными для получения рабочего кода на Lisp, а не самим рабочим кодом
хотя ты, чудо, меня игноришь :)

6yrop

"С СУЩЕСТВЕННЫМИ ограничениями по синтаксису"
собственно в интернетах так и пишут
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 мощный инструмент, но ограничен определенной синтаксической формой

Serab

это не противоречит написанному на вики, имхо

yolki

(defquote '(
Любая достаточно сложная программа на C или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common Lisp[1]
)
— '(Филип Гринспен

6yrop

как ты понимаешь написанное вики?
Собственно, я этим интересуюсь вот в каком контексте. Для работы с СУБД нужно фактически программой генерировать программу. Поскольку везде пишут, что Lisp хорош для задачи генерации программы. То почему в качестве интерфейса к СУБД не сделали Lisp? Мое имхо такое, что все же специализированный синтаксис (SQL) для людей более понятен, читаем и т.п. Я не прав? Вот тут даже lisper-ы сами говорят, что, по крайней мере, "Undoubtedly, select is harder to learn to use than query. (ref1)"

Serab

Если что, я лисп не знаю, только слышал :crazy: Я понимаю так, что можно расширять синтаксис и для этого предоставлены большие возможности, но почему отсюда должно сделовать, что можно любой язык заэмулировать, я не понимаю.

6yrop

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

6yrop

Если что, я лисп не знаю, только слышал
я тоже. Но насколько я понял, там все расширения остаются в рамках "списочной записи" — элементы списка в скобочках через пробел.

apl13

но почему отсюда должно сделовать, что можно любой язык заэмулировать, я не понимаю.
ПОТОМУ ЧТО ТЫ ЕЩЕ НЕ ПРОНИКСЯ СУЩНОСТЬЮ CONTROLLABLE QUERY!11!11 :kar: :emporerslightning: :pop:

apl13

:facepalm:

6yrop

попробуй объяснить

apl13

Мое имхо такое, что все же специализированный синтаксис (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;".
Большинство привыкло к инфиксной записи и к тому, что скобки можно ставить где и как угодно.
Поэтому лисп малопопулярен.

apl13

Ну кстати, почти что на твой вопрос можно ответить почти что положительно.
То есть, можно вполне написать на лиспе макросы, которые сделают корректными записи вроде:
(c-sharp-def '(GenericList < string > list2 = new GenericList < string > 
(c-sharp 'Console :point WriteLine ("Hello world!" (Console :point WriteLine("Farewell world!"

(необходимость пробелов между шарповыми токенами зависит от того, сколько трудолюбия ты в этот макросы вложил).
Но проще, наверное, обратиться к FFI. А то тут будет, в общем-то, интерпретация почти полная (только без токенизации текста тоже не сахар.
И да, лисп - это не совсем язык, лисп - это семейство диалектов. Не во всех из них функции являются первоклассными, в некоторых из них есть классы и объекты, не везде можно поймать продолжение, etc.

karkar

т.е. 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# сделать частью лиспа. В немерле, другом макросоориентированном языке, так и сделали примерно - запилили компилятор шарпа немерловыми средствами.

6yrop

необходимость пробелов между шарповыми токенами зависит от того, сколько трудолюбия ты в этот макросы вложил
что ты этим хочешь сказать? Ты про парсинг текста? и потерю основного приемущества 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.

6yrop

В нем есть Read Macros, которые позволяют парсить произвольный текст, не только sexp'ы.
А Read Macros позволяет вот это "the syntax allows a natural and powerful intermixing of lisp forms and SQL. (ref1)"?

6yrop

Так что при большом желании да, можно C# сделать частью лиспа. В немерле, другом макросоориентированном языке, так и сделали примерно - запилили компилятор шарпа немерловыми средствами.
хочется чтобы это сочеталось со стандартными средствами декомпозиции основного языка, например, должны работать приемы типа extract method для выделения фрагмента кода (с последующим его вызовом в нескольких местах). Иначе это просто занимательная игрушка.

karkar

Какие приемы реализуешь, такие и будут работать. Тьюринг полнота в компайл тайме и доступность компилятора в рантайме позволяют реализовать любые фантазии. Вопрос лишь в цене и целесообразности.

6yrop

реализуешь
т.е. готового ничего не получаешь? Тогда, что остается от lisp-а?

6yrop

реализуешь
сейчас интересна не реализация, а спецификация. Со списочной записью всё понятно, там работают операции над списками. А когда отходим от списочной записи, то чем это отличается от спецификации своего языка с нуля?

karkar

Фишка в том, что можно вкраплять новый синтаксис в текущий. Т.е. не просто иметь функцию "распарсить строку в AST", а например добавить новый вид строковых литералов, добавить LINQ или SQL выражения и использовать их посреди лиспового кода.
Ну и понятно, что чаще всего 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.

6yrop

а например добавить новый вид строковых литералов, добавить LINQ или SQL выражения и использовать их посреди лиспового кода.
строковые литералы ... посреди кода... Это не полные условия цели (LINQ этим и плох). Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных. Т.е. хочется иметь лисповый дуализм кода и данных, но не только для спискового синтаксиса, но и для синтаксиса SQL и т.д.

6yrop

ну и со статической типизацией, да :)

Dasar

Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных.
nemerle же все это делает - чем он тебя не устраивает?

6yrop

nemerle же все это делает - чем он тебя не устраивает?
в рантайме с SQL-ем? я такого не видел, запости ссылку.

6yrop

хочется того, что сейчас делают многие ORM где используется Query Object или Query Monad, но с человеческим синтаксисом. LINQ немного приблизил человеческий синтаксис, но потеряли работу со структурой данных в рантайме.

Dasar

в рантайме с 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

6yrop

покажи пример использования

Dasar

пример использования чего?
в предыдущем посте внутри блока grammar используется не C#-синтаксис - это пример встройки своего синтаксиса

karkar

Идеальное решение — в точности синтаксис SQL/LINQ плюс работа с этими выражениями как со структурой данных.
Именно так и будет. В процессе парсинга исходника компилятор, встретив LINQ выражение, вызывает твой read macro, тот генерит обычные деревья, они становятся частью общей структуры программы. Когда другие макросы потом что-то делают с этим кодом, они работают с привычными структурами, не подозревая, что часть этих списков была получена не чтением скобочек, а разбором LINQ выражения.
Если вдруг нужно после преобразования опять получить LINQ, на то есть pretty-printer'ы.

apl13

т.е. готового ничего не получаешь? Тогда, что остается от lisp-а?
Ты можешь определить макрос, допускающий выражения на псевдокоде:
(define-syntax pseudocode-function ...)

Затем ты его используешь:
(pseudocode-function '(Int Increase(Int x) (return x + 1'

Так вот, после экспансии макроса это не будет уже код на псевдокоде. Экспансия макроса превратит его в код на лиспе. Если имплементация компилируемая, это произойдет при компиляции. Функцию эту ты потом можешь использовать как любую другую лисповую:
> (begin (display (pseudocode-Increase 10 (newline
11

Ну то есть см. пост а над моим.

apl13

ну и со статической типизацией, да :)
Тогда не лисп.

6yrop

Именно так и будет. В процессе парсинга исходника компилятор, встретив LINQ выражение, вызывает твой read macro, тот генерит обычные деревья, они становятся частью общей структуры программы. Когда другие макросы потом что-то делают с этим кодом, они работают с привычными структурами, не подозревая, что часть этих списков была получена не чтением скобочек, а разбором LINQ выражения.
Если вдруг нужно после преобразования опять получить LINQ, на то есть pretty-printer'ы.
Моя фраза “работа с выражениями как со структурой данных” требует более детальных пояснений. Рассмотрим три варианта записи выражения “a + b + c”:

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 это вопрос…

6yrop

Еще добавлю для сравнение Code4 — код в стиле ADO.NET/JDBC/ASP/JSP и т.п.

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 (-) подверженность глупым ошибкам при написании, сложность при навигации по коду, сложность при внесении изменений.

karkar

-А можно на руку ботинок надеть?
-Можно...
-Но тогда будет неудобно ковырять в носу!
-Ну, да.
Ты чего хотел-то?

apl13

Подозреваю, что Шуреск хочет, чтобы как в функциональных языках, но чтобы без функциональных языков.
Вот и спрашивает, можно ли в функциональных языках так, чтобы без функциональных языков.

6yrop

Ты чего хотел-то?
Чтобы решение не содержало (- см. выше. Другими словами, SQL синтаксис внутри языка с возможностью extract method любого фрагмента SQL выражения. Но без побочных минусов как в Code4. Еще хорошо, если проверка синтаксиса на этапе компиляции. Ни в одном современном языке это не наблюдается C#, Java, Scala, Haskell, Lisp, Ruby, Python.

6yrop

Подозреваю, что Шуреск хочет, чтобы как в функциональных языках, но чтобы без функциональных языков.
Вот и спрашивает, можно ли в функциональных языках так, чтобы без функциональных языков.
причем тут именно функциональность не понятно, тут всё и Query Object, и монады, и "With LINQ's expression trees, they seem to tackle the same problem as Lisp does with S-expressions (ref1)". Но ни что из этого не обходится без минусов, как я описал выше.

6yrop

Ты чего хотел-то?
Например, я хочу вот так смешивать (миксовать) основной язык и 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
);
}

Dasar

А для Code2 произошла катастрофа – мы не можем пройти по “b + c” как по структуре данных.
[...]
1.3 (-) плохое смешивание с обычным кодом, extract method и т.д.
какое обоснование этого тезиса? только то, что конкретная библиотека не умеет ходить по коду методов?

6yrop

какое обоснование этого тезиса? только то, что конкретная библиотека не умеет ходить по коду методов?
на первый взгляд вроде можно и по IL ходить, но Expression<T> не просто так ввели, наверное. Тогда бы и IEnumerable вместо IQueryable можно было бы использовать. Думаю, по IL задолбаешься ходить, его для этого не делали, это хак. .Net не Lisp же :) .

Dasar

есть roslyn, который успешно ходит по с#

6yrop

ну во-первых надо в рантайме, а там C#-а уже нет, там IL

6yrop

ну а ты как думаешь, зачем они ввели Expression<T> и специальную обработку компилятором для него?

Dasar

ну во-первых надо в рантайме, а там C#-а уже нет, там IL
вообще, reflector же осиливает преобразование IL -> C#, и вполне адекватно. Поэтому я не вижу в чем проблема?..

Dasar

ну а ты как думаешь, зачем они ввели Expression<T> и специальную обработку компилятором для него?
потому что хотели быстро решить частную задачу в виде запиливания sql-linq

6yrop

частную задачу в виде запиливания linq
что за задача? формулировка слишком расплывчета.

6yrop

sql-linq
так понятнее

Dasar

что за задача?
поправился: sql-linq.
Им было лень делать полноценный анализатор IL-а в виде высокоуровневого преобразователя (хотя reflector показывает что это возможно и вместо этого быстро вкосячили костыль в виде Expression

6yrop

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

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-сервер?

Dasar

по хитрому алгоритму его частично выполнять в CLR-е
весь хитрый алгоритм состоит из осознания, что есть структура вида: tree<expression>, и что для каждого expression либо есть аналог в sql, и тогда его можно перевести в sql и там выполнить, либо аналога нет, и его необходимо выполнять на уровне кода.

6yrop

весь хитрый алгоритм состоит из осознания, что есть структура вида: tree<expression>, и что для каждого expression либо есть аналог в sql, и тогда его можно перевести в sql и там выполнить, либо аналога нет, и его необходимо выполнять на уровне кода.
имхо, что выполнять, а что отправлять должен решать программист-пользователь. Иначе работа с СУБД будет неэффективной (что часто встречается даже в сегодняшней реализации linq). Всё выражение "DateTime.Now.Day > 13 ? b + c : b;" имеет аналог в sql, но условный оператор можно выполнить в CLR, Code3 так и делает, и из кода намеренья программиста четко просматриваются.

Dasar

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

6yrop

кстати, вот то, о чем ты говоришь http://www.cs.utexas.edu/~wcook/papers/SafeQuery05/SafeQuery...
Ты рискнешь это использовать в коммерческом проекте?

luna89

Когда MS объявит, что LINQ не оправдал ожиданий и вообще теперь вместо шарпа яваскрипт, то все это творчество придется выкинуть на помойку. В то же время, приложения, написанные в 80-е на plsql, выглядят сейчас абсолютно современно (и будут также выглядеть через 20 лет).

Dasar

В то же время, приложения, написанные в 80-е на plsql, выглядят сейчас абсолютно современно (и будут также выглядеть через 20 лет).
ты так говоришь, как будто это хорошо.
Скорее это обычно говорит о застое в определенной области.

luna89

Скорее это обычно говорит о застое в определенной области.
А чего тебе не хватает в SQL и PL\SQL?

Dasar

А чего тебе не хватает в SQL и PL\SQL?
высокоуровневой параметризации - что обычно и пытаются достичь с помощью кучи разных способов. в том числе, с помощью sql-linq

luna89

высокоуровневой параметризации - что обычно и пытаются достичь с помощью кучи разных способов. в том числе, с помощью sql-linq

Делается вьюха, в которой выставляются все столбцы, по которым надо фильтровать. Корректность вьюхи проверяется в compile time сервером СУБД. После этого генерация sql сводится к добавлению условий вида
  and ( column1 = :param1 ) 
Таким образом, динамическая типизация осталась, но ее очень немного и на практике никаких проблем она не создаст. В конце концов, в том же шарпе вместо значения в функцию всегда может прийти null. Это тоже своего рода динамическая типизация. Но почему-то в данном случае никто не городит монструозные фреймворки для борьбы с ней.

Dasar

После этого генерация sql сводится к добавлению условий вида
это только верхушка айсберга для высокоуровневой параметризации.
Но почему-то в данном случае никто не городит монструозные фреймворки для борьбы с ней.
их сотни! Того же Липперта который год просят сделать краткий синтаксис для быстрой обработки null-я, но он пока упирается

luna89

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

Dasar

даже разные виды сортировки к этому не сводятся

6yrop

Можешь привести реалистичный пример, несводимый к тому что я написал?
Из тех, что лежат в общем доступе:
Пример1
Пример2, см. Раздел 1
У нас на работе таких примеров много, это обычные будничные ситуации.

6yrop

Таким образом, динамическая типизация осталась, но ее очень немного и на практике никаких проблем она не создаст.
Сильно субъективное мнение. Если ты считаешь его верным, опубликуйся где-нибудь. Получишь фитбек, и поймешь что в реальности всё разнообразнее. Вот люди публикуются, и у них вот такое мнение:
 
Встраиваемый SQL обеспечивает статически типизированный подход к явному выполнению запросов. Как обсуждается в разд. 7, одним из существенных недостатков встраивания является отсутствие поддержки динамических запросов.
...
Мы полагаем, что отсутствие поддержки динамических запросов является основной причиной отказа от большинства форм встраиваемого SQL [27].
http://citforum.ru/database/articles/impedance_mismatch/
  

6yrop

в функцию всегда может прийти null
это косяк C#-а, исправлять его сейчас не будут, поскольку уже поздно. В хорошем коде null-ы стараются не использовать. В новом языке Kotlin это решается на уровне языка. Мы используем Option Type. В Scala, F# тоже используются Option Type.
Оставить комментарий
Имя или ник:
Комментарий: