Здравствуйте, z00n, Вы писали:
Z>Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>>Ещё в качестве альтернативы предлагаю вот этот микробенчмарк, названный Jon Harrop Challenge Problem . Z>Отличная мысль. Аналогично, Одерски как-то хвастался перед явистами паттерн-матчингом на таком примере: Z>
Z>// SCALA!
Z>class Term
Z>case class Num(n: int) extends Term
Z>case class Var(name: String) extends Term
Z>case class Mul(l: Term, r: Term) extends Term
Z>case class Add(l: Term, r: Term) extends Term
Z>// Now let's say we want to implement some simplification rules on such terms.
Z>// Two useful simplifications apply the equalities:
Z>// 0 * x = 0
Z>// 1 * x = x
Z>def simplify(term: Term) = term match {
Z> case Mul(Num(0), x) => Num(0)
Z> case Mul(Num(1), x) => x
Z> case _ => term
Z>}
Z>
Z>Ему ответили, что мултиплай — диспатч на яве — это просто
В чем принципиальное отличие от такого кода на Яве?
public Term simplify(Term term) {
_Term x = new _Term();
if (term.equals(new Mul(new Num(0), x))) return new Num(0);
if (term.equals(new Mul(new Num(1), x))) return x.value;
return term;
}
Z>class Term
Z>case class Num(n: int) extends Term
Z>case class Var(name: String) extends Term
Z>case class Mul(l: Term, r: Term) extends Term
Z>case class Add(l: Term, r: Term) extends Term
Z>def simplify(term: Term) = term match {
Z> case Mul(Num(0), x) => Num(0)
Z> case Mul(Num(1), x) => x
Z> case _ => term
Z>}
Z>>Ему ответили, что мултиплай — диспатч на яве — это просто SR>В чем принципиальное отличие от такого кода на Яве?
SR> public Term simplify(Term term) {
SR> _Term x = new _Term();
SR> if (term.equals(new Mul(new Num(0), x))) return new Num(0);
SR> if (term.equals(new Mul(new Num(1), x))) return x.value;
SR> return term;
SR> }
Отличия колоссальные:
1. Паттерн-матчинг осуществляет разбор одновременно с биндингом, так что в первом случае x получит в качестве значения всё поддерево, чего бы там ни было
2. Паттерн-матчинг не пересоздаёт выражения чтобы выяснить соответствие выражений паттерну
3. Алгоритмы паттерн-матчинга не выясняют структуры подвыражений, если структура стала известна на предыдущих шагах
4. Паттерн-матчинг может содержать вайлдкарды и гварды
5. Паттерн-матчинг может быть исследован на полноту в процессе компиляции
Для окончательного прояснения различия я декомпилировал код на Скале, функция simplify в данном случае будет выглядеть так:
public Term simplify(Term term)
{
Term term1 = term;
Term term4;
if(term1 instanceof Mul)
{
Mul mul = (Mul)term1;
Term term2 = mul.l();
Term term3 = mul.r();
if(term2 instanceof Num)
{
switch(((Num)term2).n())
{
default:
break;
case 0: // '\0'
term4 = new Num(0);
break;
case 1: // '\001'
term4 = term3;
break;
}
}
}
term4 = term;
return term4;
}
Стоит чуть-чуть навернуть simplify,
def simplify(term: Term) = term match {
case Mul(Num(0), x) => Num(0)
case Mul(Num(1), x) => x
case Add(Num(0), f) => f
case Add(f, Num(0)) => f
case _ => term
}
и чтобы сэмулировать это на Йаве, потребуется небольшой такой напряг (кстати, у jad-а при декомпиляции пупок надорвался):
Здравствуйте, Lazy Cjow Rhrr, Вы писали: LCR>Можно попробовать перевести на человеческий, это будет каскад вложенных условий, но сейчас времени нет.
У меня есть самопальный компилятор, который умеет переписывать патернматчинг как каскад вложенных if-else, правда таргетит язык с динамической типизацией(Lua) — что очевидным образом увеличивает количество тестов. Вообще, конечно, свитчи или goto лучше, но в луа goto есть только в байткоде, а мне хотелось сохранить минимальную читаемость. Использован модифицированный алгоритм Sestoft'а, как в MosML, MLKit, Nemerle.
Итак исходный пример переписанный на неком надмножестве луа(похоже на Ocaml), добавил рекурсию:
require'zstd'
local ppp = zstd.ppp
-- Scala:
-- def simplify(term: Term) = term match {
-- case Mul(Num(0), x) => Num(0)
-- case Mul(Num(1), x) => x
-- case Add(Num(0), f) => f
-- case Add(f, Num(0)) => f
-- case _ => term
-- }
local fun simplify
| {"Mul",{"Num",0},x} -> return {"Num",0}
| {"Mul",{"Num",1},x} -> return simplify(x)
| {"Add",{"Num",0},f} -> return simplify(f)
| {"Add",f,{"Num",0}} -> return simplify(f)
| term -> return term
end
-- TEST:
ppp(1, simplify({"Mul",{"Num",1},"X"})) --> "X"
ppp(2, simplify({"Mul",{"Num",0},"X"})) --> {"Num",0}
ppp(3, simplify({"Add",{"Num",0},"F"})) --> "F"
ppp(4, simplify({"Add","F",{"Num",0}})) --> "F"
ppp(5, simplify({"Mul",{"Num",1}, {"Add",{"Num",0},"F"}})) --> "F"
Вот как выглядит результат компиляции функции simplify на Lua 5.1
local function simplify(_u0)
if type(_u0) == 'table' then
if #_u0 == 3 then
if _u0[1] == "Mul" then
if type(_u0[2]) == 'table' then
if #_u0[2] == 2 then
if _u0[2][1] == "Num" then
if _u0[2][2] == 0 then return {"Num";0}
else
if _u0[2][2] == 1 then return simplify(_u0[3])
else return _u0
end;
end;
else return _u0
end;
else return _u0
end;
else return _u0
end;
else
if _u0[1] == "Add" then
if type(_u0[2]) == 'table' then
if #_u0[2] == 2 then
if _u0[2][1] == "Num" then
if _u0[2][2] == 0 then return simplify(_u0[3])
else
if type(_u0[3]) == 'table' then
if #_u0[3] == 2 then
if _u0[3][1] == "Num" then
if _u0[3][2] == 0 then
return simplify(_u0[2])
else return _u0
end;
else return _u0
end;
else return _u0
end;
else return _u0
end;
end;
else
if type(_u0[3]) == 'table' then
if #_u0[3] == 2 then
if _u0[3][1] == "Num" then
if _u0[3][2] == 0 then
return simplify(_u0[2])
else return _u0
end;
else return _u0
end;
else return _u0
end;
else return _u0
end;
end;
else
if type(_u0[3]) == 'table' then
if #_u0[3] == 2 then
if _u0[3][1] == "Num" then
if _u0[3][2] == 0 then
return simplify(_u0[2])
else return _u0
end;
else return _u0
end;
else return _u0
end;
else return _u0
end;
end;
else
if type(_u0[3]) == 'table' then
if #_u0[3] == 2 then
if _u0[3][1] == "Num" then
if _u0[3][2] == 0 then
return simplify(_u0[2])
else return _u0
end;
else return _u0
end;
else return _u0
end;
else return _u0
end;
end;
else return _u0
end;
end;
else return _u0
end;
else return _u0
end;
end;
Справка по синтаксису Луа:
-- комментарий
x = {1,2,{11,12}} -- x массив из 3-х элементов, индексы начинаются с 1.
#x == 3 -- # оператор взятия длинны
x[3][2] == 12 -- сабскрипт
Re[2]: Паттерн-матчинг - отличие от обычных конструкций
LCR>Отличия колоссальные:
Речь шла про синтаксис, а в этом случае различия малосущественные. LCR>1. Паттерн-матчинг осуществляет разбор одновременно с биндингом, так что в первом случае x получит в качестве значения всё поддерево, чего бы там ни было
Также и в моем примере.
LCR>2. Паттерн-матчинг не пересоздаёт выражения чтобы выяснить соответствие выражений паттерну LCR>3. Алгоритмы паттерн-матчинга не выясняют структуры подвыражений, если структура стала известна на предыдущих шагах
Это лишь вопросы (2. и 3.) производительности, соответственно достаточно интеллектуальный компилятор, особенно если ему помочь аннотацией, вполне способен развернуть equals в набор if'ов. В расширении же синтаксиса и тем более в переходе на новый язык нет необходимости.
LCR>4. Паттерн-матчинг может содержать вайлдкарды и гварды
Wildcard есть и в моем примере, это переменная x.
Guard же это вообще насколько я понимаю практически чистый if или оператор ?:.
LCR>5. Паттерн-матчинг может быть исследован на полноту в процессе компиляции
Да, это определенное преимущество.
Но опять же, достаточно интеллектуальный компилятор java может провести подобное исследование.
Что-то в духе:
boolean is(int number) {
if (number > 0)
return true;
if (number <= 0)
return false;
// отсутствует ошибка, что "This method must return a result of type boolean."
}
С другой стороны в примере на Scala такой проверки нет использутся case _.
LCR>Для окончательного прояснения различия я декомпилировал код на Скале, функция simplify в данном случае будет выглядеть так:
Я тоже это проделывал, очевидно, что Jad'у не удалось получить правильно работающий код.
LCR>
LCR>def simplify(term: Term) = term match {
LCR> case Mul(Num(0), x) => Num(0)
LCR> case Mul(Num(1), x) => x
LCR> case Add(Num(0), f) => f
LCR> case Add(f, Num(0)) => f
LCR> case _ => term
LCR>}
LCR>
LCR>и чтобы сэмулировать это на Йаве, потребуется небольшой такой напряг (кстати, у jad-а при декомпиляции пупок надорвался):
Никакого напряга не потребуется.
public Term simplify(Term term) {
_Term x = new _Term();
if (term.equals(new Mul(new Num(0), x)))
return new Num(0);
if (term.equals(new Mul(new Num(1), x))
return x.value;
_Term f = new _Term();
if (term.equals(new Add(new Num(0), f))
return f.value;
if (term.equals(new Add(f, new Num(0)))
return f.value;
return term;
}
Правда и пример ничем существенно не отличается от предыдущего.
В исходном сообщении Одерски есть ссылка на гораздо более сложный simplify, тем не менее он тоже вполне успешно воспроизводится.
Re[3]: Паттерн-матчинг - отличие от обычных конструкций
SmbdRsdn,
SR>Речь шла про синтаксис, а в этом случае различия малосущественные.
Даже если ограничиться синтаксисом, то ПМ выглядит чище. Плюс отсутствует дополнительный уровень косвенности, который привносит equals.
LCR>>1. Паттерн-матчинг осуществляет разбор одновременно с биндингом, так что в первом случае x получит в качестве значения всё поддерево, чего бы там ни было SR>Также и в моем примере.
Тогда ты забыл привести определение метода equals. (Сдаётся мне, что он будет иметь побочные эффекты, и потребуется сбрасывать x перед каждым новым if).
LCR>>2. Паттерн-матчинг не пересоздаёт выражения чтобы выяснить соответствие выражений паттерну LCR>>3. Алгоритмы паттерн-матчинга не выясняют структуры подвыражений, если структура стала известна на предыдущих шагах SR>Это лишь вопросы (2. и 3.) производительности, соответственно достаточно интеллектуальный компилятор, особенно если ему помочь аннотацией, вполне способен развернуть equals в набор if'ов. В расширении же синтаксиса и тем более в переходе на новый язык нет необходимости.
Игнорирование вопросов производительности приводит к тому, что приходится делать квадратичное количество сравнений вместо линейного. Никакой интеллектуальный компилятор не сможет избежать создания трёх объектов ("new Mul(new Num(0), x)"), ибо они передаются в функцию. Короче, зачем нужны кривые подпорки, если есть прямое средство?
LCR>>4. Паттерн-матчинг может содержать вайлдкарды и гварды SR>Wildcard есть и в моем примере, это переменная x. SR>Guard же это вообще насколько я понимаю практически чистый if или оператор ?:.
Добро пожаловать в Turing tarpit.
LCR>>5. Паттерн-матчинг может быть исследован на полноту в процессе компиляции SR>Да, это определенное преимущество. SR>Но опять же, достаточно интеллектуальный компилятор java может провести подобное исследование. SR>Что-то в духе: SR>
SR>boolean is(int number) {
SR> if (number > 0)
SR> return true;
SR> if (number <= 0)
SR> return false;
SR> // отсутствует ошибка, что "This method must return a result of type boolean."
SR>}
SR>
SR>С другой стороны в примере на Scala такой проверки нет, и использутся case _.
Плохие новости номер раз. Сейчас достаточно интеллектуального компилятора Йавы нет и учитывая скорость развития её в ближайшую декаду не предвидится.
Плохие новости номер два. Даже если он вдруг неожиданно появится, есть принципиальные моменты, в которые этот компилятор упрётся. Например, попробуй хотя бы сам доказать, что здесь не нужно сообщение "this method must return bla-bla-bla..."
Теорему Райса ещё никто не отменял, и "интеллектуальному компилятору" это тоже не удастся.
Плохие новости номер три. Очевидным решением будет "попробовать проверить эти if-ы, если удастся что-нибудь доказать, то выдать результат". Это решение будет в 99% выдавать "не смог", потому что синтаксис if-ов общий, и компилятор не может делать консервативные предположения, в отличие от паттерн-матчинга, где синтаксис может выбран так, чтобы с одной стороны он был как можно более общий, а с другой, чтобы он оставался разрешимым относительно exhaustiveness checking.
Плохие новости номер четыре. В Scala такая проверка таки есть:
sealed abstractclass Term
case class Num(n: int) extends Term
case class Var(name: String) extends Term
case class Mul(l: Term, r: Term) extends Term
case class Add(l: Term, r: Term) extends Term
class App extends Application
{
def simplify(term: Term) = term match {
case Mul(Num(0), x) => Num(0)
case Mul(Num(1), x) => x
case Add(Num(0), f) => f
case Add(f, Num(0)) => f
// case _ => term
}
}
имеем
test2.scala:16: warning: match is not exhaustive!
missing combination Num
missing combination Var
def simplify(term: Term) = term match {
^
one warning found
LCR>>и чтобы сэмулировать это на Йаве, потребуется небольшой такой напряг (кстати, у jad-а при декомпиляции пупок надорвался): SR>Никакого напряга не потребуется. SR>
SR>public Term simplify(Term term) {
SR>_Term x = new _Term();
SR>if (term.equals(new Mul(new Num(0), x)))
SR> return new Num(0);
SR>if (term.equals(new Mul(new Num(1), x))
SR> return x.value;
SR>_Term f = new _Term();
SR>if (term.equals(new Add(new Num(0), f))
SR> return f.value;
SR>if (term.equals(new Add(f, new Num(0)))
SR> return f.value;
SR>return term;
SR>}
SR>
SR>Правда и пример ничем существенно не отличается от предыдущего.
Сделай мне, чтобы твой код имел тот-же порядок сложности, что и ПМ, а то я тут вижу слишком много лишних действий. Ну примерно так:
public Term simplify(Term term)
{
if(term instanceof Mul)
{
Mul term2 = (Mul) term;
Term term3 = term2.l();
Term term4 = term2.r();
if (term3 instanceof Num)
{
switch(((Num)term3).n())
{
case 0:
return new Num(0);
case 1:
return term4;
default:
return term;
}
}
}
else if (term instanceof Add)
{
Add term2 = (Add) term;
Term term3 = term2.l();
Term term4 = term2.r();
if (term3 instanceof Num && ((Num)term3).n() == 0)
return term4;
else if (term4 instanceof Num && ((Num)term4).n() == 0)
return term3;
}
return term;
}
Никаких лишних сравнений подвыражений на instanceof, и, где это выгодно, используется switch.
BTW, если бы я вынужден был бы оставаться в Йаве, то вместо изобретения велосипеда я бы просто взял готовую библиотеку с реализацией какого-нибудь алгоритма ПМ, например Tom.
z00n,
Z>У меня есть самопальный компилятор, который умеет переписывать патернматчинг как каскад вложенных if-else, правда таргетит язык с динамической типизацией(Lua) — что очевидным образом увеличивает количество тестов. Вообще, конечно, свитчи или goto лучше, но в луа goto есть только в байткоде, а мне хотелось сохранить минимальную читаемость. Использован модифицированный алгоритм Sestoft'а, как в MosML, MLKit, Nemerle.
Круто!
То есть, как я понял, ты какое-то время писал на голом Луа, потом тебя это достало, и ты решил создать метаязык для Луа, так? Паттерн-матчинг значит там уже есть, а ещё что? ФВП, list comprehensions, continuations?
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Даже если ограничиться синтаксисом, то ПМ выглядит чище.
Или излишне сокращенно, короче дело вкуса. LCR>Плюс отсутствует дополнительный уровень косвенности, который привносит equals.
Или присутствует дополнительный уровень явности — программа читается как текст, что является преимуществом.
Название метода даже можно заменить на matches или что-то вроде этого.
LCR>Тогда ты забыл привести определение метода equals. (Сдаётся мне, что он будет иметь побочные эффекты, и потребуется сбрасывать x перед каждым новым if).
Я не забыл, просто для обсуждения возможного синтаксиса PM в Java он не существенен.
А сбрасывать нет необходимости, значение там появится только когда будет совпадение с образцом.
LCR>Игнорирование вопросов производительности приводит к тому, что приходится делать квадратичное количество сравнений вместо линейного. Никакой интеллектуальный компилятор не сможет избежать создания трёх объектов ("new Mul(new Num(0), x)"), ибо они передаются в функцию. Короче, зачем нужны кривые подпорки, если есть прямое средство?
Вопросы производительности не нужно игнорировать, а нужно как и в большинстве случаев при разработке ПО откладывать.
Особого интеллекта компилятору не нужно, в теме уже приводились примеры развертывания PM на блок if'ов.
SR>>Guard же это вообще насколько я понимаю практически чистый if или оператор ?:. LCR> Добро пожаловать в Turing tarpit.
Гм? Как раз Guard переносится на Java один в один, что тут загадочного или не с ходу понятного?
LCR>Плохие новости номер раз. Сейчас достаточно интеллектуального компилятора Йавы нет и учитывая скорость развития её в ближайшую декаду не предвидится.
Так как синтаксис не меняется, то тут достаточно компиляторы из Java в Java. А отличных парсеров Java предостаточно, пока думаю остановится на ASTParser из eclipse.
LCR>Плохие новости номер два. Даже если он вдруг неожиданно появится, есть принципиальные моменты, в которые этот компилятор упрётся. Например, попробуй хотя бы сам доказать, что здесь не нужно сообщение "this method must return bla-bla-bla..." LCR>
LCR>Теорему Райса ещё никто не отменял, и "интеллектуальному компилятору" это тоже не удастся.
А зачем Java бежать впереди паровоза? Пусть сначала подобная возможность появится в Scala.
LCR>Плохие новости номер три. Очевидным решением будет "попробовать проверить эти if-ы, если удастся что-нибудь доказать, то выдать результат". Это решение будет в 99% выдавать "не смог", потому что синтаксис if-ов общий, и компилятор не может делать консервативные предположения, в отличие от паттерн-матчинга, где синтаксис может выбран так, чтобы с одной стороны он был как можно более общий, а с другой, чтобы он оставался разрешимым относительно exhaustiveness checking.
В обобщенном применении и нет необходимости. Достаточно если будут обрабатываться методы помеченные определенным атрибутом и if'ы имеющие определенную форму.
LCR>Плохие новости номер четыре. В Scala такая проверка таки есть: LCR>
LCR>class App extends Application
LCR>{
LCR> def simplify(term: Term) = term match {
LCR> case Mul(Num(0), x) => Num(0)
LCR> case Mul(Num(1), x) => x
LCR> case Add(Num(0), f) => f
LCR> case Add(f, Num(0)) => f
LCR>// case _ => term
LCR> }
LCR>}
LCR>
LCR>имеем LCR>
LCR>test2.scala:16: warning: match is not exhaustive!
LCR>missing combination Num
LCR>missing combination Var
LCR> def simplify(term: Term) = term match {
LCR> ^
LCR>one warning found
LCR>
По приведенному-то коду не особенно впечатляюще. Вот если бы не было case_ и образцы были бы посложнее.
SR>>Правда и пример ничем существенно не отличается от предыдущего. LCR>Сделай мне, чтобы твой код имел тот-же порядок сложности, что и ПМ, а то я тут вижу слишком много лишних действий. Ну примерно так:
Я уже писал, это дело компилятора. Впрочем возможно я займусь подобной оптимизацией в ближайшее время.
LCR>BTW, если бы я вынужден был бы оставаться в Йаве, то вместо изобретения велосипеда я бы просто взял готовую библиотеку с реализацией какого-нибудь алгоритма ПМ, например Tom.
Неужели, думаете, что начиная рассуждать о PM в Java я не был в курсе про Tom?
К тому же PM в Tom мощнее того, что в Scala.
Re[5]: Действительно ли ML языки хороши в компиляторописани
Здравствуйте, SmbdRsdn, Вы писали:
SR>По приведенному-то коду не особенно впечатляюще. Вот если бы не было case_ и образцы были бы посложнее.
case _ и не было, но простоту образцов это не отменяет.
Re[4]: Паттерн-матчинг - отличие от обычных конструкций
Здравствуйте, Lazy Cjow Rhrr, Вы писали: LCR>Круто!
Спасибо
LCR>То есть, как я понял, ты какое-то время писал на голом Луа, потом тебя это достало, и ты решил создать метаязык для Луа, так? Паттерн-матчинг значит там уже есть, а ещё что? ФВП, list comprehensions, continuations?
Паттерн матчинг для луа я увидел в metalua, но он был реализован наивно(проверяем все подряд пока не false, goto следующая строка) — захотелось сделать лучше
ФВП — там уже есть, вместо continuations есть coroutines, вместо list comprehensions есть итераторы. Я добавил поддержку списков в стиле ML, что вкупе с паттерн-матчингом позволило мне воровать достаточно нетривиальные программы написанные на Haskell или Ocaml.
-- Сердце Вадлеровского претти-принтера украденное одновременно у Линдига(Quest)
-- и у Daan Leijen (Haskell PPrint)
-- best : (sb,int,int,Cells) -> sb
-- @sb - string buffer
-- @n - indentation of current line
-- @k - current column
-- Cells : listof {indent, Mode, Doc}
local function best(sb, n, k, cells) case cells of
| [] -> return sb
| {i,_,Empty} :: z -> return best(sb,n,k,z)
| {i,m,{'Cons',x,y}} :: z -> return best(sb,n,k,{i,m,x}::{i,m,y}::z)
| {i,m,{'Nest',j,x}} :: z -> return best(sb,n,k,{i+j,m,x}::z)
| {i,_,{'Text',s}} :: z -> return best(emit(sb,s),n,k+#s,z)
| {i,FLT,{'Break',s}} :: z -> return best(emit(sb,s),n,k+#s,z)
| {i,BRK,{'Break',_}} :: z -> return best(nl(sb,i),i,i,z)
| {i,FLT,{'Group',x}} :: z -> return best(sb,w,k,{i,FLT,x}::z)
| {i,BRK,{'Group',x}} :: z ->
local ribbonleft = (w - k) `min` (ribbon - k + n)
if fits(ribbonleft,k,{i,FLT,x}::z) then
return best(sb,n,k,{i,FLT,x}::z)
else
return best(sb,n,k,{i,BRK,x}::z)
end
| {i,m,{'Column',f}} :: z -> return best(sb,n,k,{i,m,f(k)}::z)
| {i,m,{'Nesting',f}} :: z -> return best(sb,n,k,{i,m,f(i)}::z)
| x -> error(tostring(x))
end
end
-- return of layout
return best(sb,0,0,{0,BRK,doc}::[])
Ну и по мелочи: добавил инфиксные-префиксные операторы в стиле Scala, компактную форму для thunks, etc
LCR>>Даже если ограничиться синтаксисом, то ПМ выглядит чище. SR>Или излишне сокращенно, короче дело вкуса. LCR>>Плюс отсутствует дополнительный уровень косвенности, который привносит equals. SR>Или присутствует дополнительный уровень явности — программа читается как текст, что является преимуществом. SR>Название метода даже можно заменить на matches или что-то вроде этого.
Дело далеко не в названии метода, а в его реализации
SmbdRsdn,
LCR>>Тогда ты забыл привести определение метода equals. (Сдаётся мне, что он будет иметь побочные эффекты, и потребуется сбрасывать x перед каждым новым if). SR>Я не забыл, просто для обсуждения возможного синтаксиса PM в Java он не существенен. SR>А сбрасывать нет необходимости, значение там появится только когда будет совпадение с образцом.
А решать вопрос "сбрасывать или нет" каждый раз должен программист? Нет, спасибо, и так хватает головной боли.
SR>А зачем Java бежать впереди паровоза? Пусть сначала подобная возможность появится в Scala.
При чём здесь Scala, мы же говорили о "достаточно интеллектуальном компиляторе для явы"? Скала и не претендовала на звание "обобщённого упрощателя условных выражений".
SR>В обобщенном применении и нет необходимости. Достаточно если будут обрабатываться методы помеченные определенным атрибутом и if'ы имеющие определенную форму.
А! Вот оно. Всё понятно, это препроцессор для явы. Специально помеченные if-ы (которые, заметим, работают совсем не так как выглядят!) будут прогоняться через препроцессор, а последний будет выплёвывать уже явовский код, который потом будет компилироваться с помощью javac. Плохо.
SR>По приведенному-то коду не особенно впечатляюще. Вот если бы не было case_ и образцы были бы посложнее.
Если проверка на полноту реализована (а это так), то она будет работать и с простым кодом, и со сложным, и с одноэтажным, и с многоэтажным, и с невпечатляющим, и с впечатляющим. С любым, если код синтаксически корректен. Просто по определению... Создай сам код для ПМ, впечатляющий тебя, и впечатлись
LCR>>BTW, если бы я вынужден был бы оставаться в Йаве, то вместо изобретения велосипеда я бы просто взял готовую библиотеку с реализацией какого-нибудь алгоритма ПМ, например Tom. SR>Неужели, думаете, что начиная рассуждать о PM в Java я не был в курсе про Tom? SR>К тому же PM в Tom мощнее того, что в Scala.
(Кста, на этом форуме всё же приятнее обращаться на "ты"). Если ты в курсе про Tom, тогда в курсе, что он тоже неидеален, и по сравнению со Скалой проигрывает по многим параметрам. Возможно в отношении ПМ он и мощнее (хотя мне это неочевидно, так как в Скале есть unapply, который открывает очень богатые возможности которых, кажется, хватит за глаза любому приложению), но в отношении остальных вещей остановился на уровне обычной явы.
И я так и не понял, в чём преимущества твоего решения (которого кстати я по-прежнему не увидел, даже наброска, а имею возможность лишь косвенно догадываться по кусочкам кода и брошенным фразам)? Ы?
Сравнить поле field_name с полем field2_name (также можно использовать '=', '/=', '<', '=<', '>', '>=' )
{field_name, {'=', field2_name}}
Сравнить значение поля с любым другим значением:
{field_name, {'=', Value}}
Использовать callback-функцию. Функция может быть лямбдой или любой функцией из любого модуля в виде module:function/2 или {module, function}
{field_name, F}
Использовать callback-функцию с дополнительным значением. Функция может быть лямбдой или любой функцией из любого модуля в виде module:function/3 или {module, function}
{field_name, {F, Value}}
Ну и как написать term.equals на каждое из правил? Я предположил, что для этого каждое правило придется представлять в виде класса, отнаследованного от некоторого абстрактного Rules. С паттерн-матчингом это тривиально:
%%{field}
validate_rule({FieldName}) ->
ok.
%%{field, Func}
validate_rule({FieldName, Func}) when is_function(Func) ->
ok.
%%{field, {'=', field2}}
validate_rule({FieldName, {Operator, FieldName2}}) ->
ok.
и так далее.
При этом при отсутствии ПМ на каждый сулчай, когда придется что-то матчить, придется писать новые классы с новым equals. В случае с ПМ можно сразу обработать структуру практически любой сложности.
Здравствуйте, Mamut, Вы писали:
M>>>Дело далеко не в названии метода, а в его реализации SR>>А в чем дело с реализацией?
M>Возьму из своего же примера
Пример слабопонятен. Вернее решение для него.
Что не так, например, в таком решении, если надо всего лишь проверить ввод?
Приведенный вариант просто вернет true или false. Мой вариант вернет список не прошедших валидацию полей и всех ошибок, которые с ними связаны (правила валидации могут передаваться списком).
Более того, проверка полей может отдаваться на растерзание внешним функциям.
А что, если полей станет больше? Как написать общую функцию, которая:
— примет неограниченый список полей;
— для кажого поля можно передать от 0 до бесконечного количества правил;
— каждое правило позволяет отдать проверку внешней функции или устроить сравнение с переданым значением или другим полем;
— возвращает осмысленный список полей, не пршедших валидацию, со списком ошибок для каждого поля?
process_signup(A) ->
ValidateLength = fun(A, Field) ->
%% проверяем на длину
end,
EmailCheck = fun(Args, Field2) ->
%% проверяем правильность мыла
end,
buktu_form:validate(A, [
{login, F}, %% проверяем с использованием внешней функции
{email, EmailCheck}, %% проверяем с использованием внешней функции
{password, [{'=', password_repeat}, F]} %% проверяем на равенство с другим полем
%% а потом - с использованием внешней функции
]).
При этом функция — достаточно общая, позволяет передавать 5 видов различных правил, причем по нескольку штук на поле.
Расширим пример. У нас появится пункт о лицензионном соглашении, еще несколько checkbox'ов, которые необходимо отметить, а также год рождени, который должен быть раньше 1980-го года:
validate([
{registration},
{checkbox1},
{checkbox2},
{year, {'<', 1980}}
{login, F}, %% внешние функции остаются те же
{email, EmailCheck},
{password, [{'=', password_repeat}, F]}
])
Причем полученый список ошибок разбрать не просто легко, а очнь легко ПМ по [H|T] по списку и вперед
А функцию можно спокойно дописать для подержки более сложных правил. Например:
%% {field, {Rule, and, Rule2}}
%% оба правила должны сработать
validate(FieldName, {Rule1, and, Rule2}) ->
%% разбиваем на валидацию двух правил
validate({FieldName, Rule}) == {} andalso validate({FieldName, Rule2}) == {}
%% {field, {Rule, or, Rule2}}
%% одно из правил должно сработать
validate(FieldName, {Rule1, or, Rule2}) ->
%% разбиваем на валидацию двух правил
validate({FieldName, Rule}) == {} orelse validate({FieldName, Rule2}) == {}
При этом функция остается общей и может использоваться для любых полей любой веб-формы. Без ПМа здесь только один выход — создание некоего движка правил. Но это — совсем неудобно
[cut]
M>При этом функция остается общей и может использоваться для любых полей любой веб-формы. Без ПМа здесь только один выход — создание некоего движка правил. Но это — совсем неудобно
Да нет, Дим, про "совсем" ты тут зря, некоторые ёжики продолжают колоться и лезть на кактус. Ну нравится им это и ничего ты с этим не сделаешь...
Re[6]: Действительно ли ML языки хороши в компиляторописани
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>SmbdRsdn,
LCR>А решать вопрос "сбрасывать или нет" каждый раз должен программист?
Нет. Сбрасывать не зачем.
LCR>При чём здесь Scala,
Я написал свой ответ на сравнение сопоставления с образцом в Scala и в Java.
LCR>мы же говорили о "достаточно интеллектуальном компиляторе для явы"?
Но и не об обобщенно-интеллектуальном на все случаи жизни.
Я, конечно, понимаю, отчего возникло непонимание.
Мне было лень писать что-то вроде:
abstract class Term {};
class Num extends Term {};
class Var extends Term {}
@Exhaustive
Term simplify(Term term) {
if (term instanceof Num)
return ...;
if (term instanceof Var)
return ...;
// отсутствует "This method must return a result of type Term."
}
для показа как может выглядеть проверка на полноту при сопоставлении с образцом в Java.
LCR>А! Вот оно. Всё понятно, это препроцессор для явы.
Термин препроцессор перегружен не в том направлении.
LCR>Специально помеченные if-ы
Не отдельные if, а отдельный метод. Помеченный аннотацией. Что является законным способом добавления мета-данных в Java.
LCR>(которые, заметим, работают совсем не так как выглядят!)
Работают так же как и выглядят. То есть делают тоже, что и написано. Просто быстрее.
К тому же в Java сообществе имеется много материалов, о том как написать свой equals и для каких целей это может потребоваться.
То есть код вида
if (object.equals(new Object(...))
достаточно широко распространен.
LCR>будут прогоняться через препроцессор, а последний будет выплёвывать уже явовский код, который потом будет компилироваться с помощью javac. Плохо.
зачем прогоняться? В ASTParser можно на лету заменять отдельные ветки на другие. Короче, в чем-то аналог R#.
LCR>Если проверка на полноту реализована (а это так), то она будет работать и с простым кодом, и со сложным, и с одноэтажным, и с многоэтажным, и с невпечатляющим, и с впечатляющим.
Вот именно. Я это знаю, поэтому меня и удивило, что демонстрация проверки полноты производилась на сравнительно простом примере.
LCR>(Кста, на этом форуме всё же приятнее обращаться на "ты").
Я знаю, но предпочитаю обращаться на Вы. Впрочем не требую того же в ответ.
LCR>И я так и не понял, в чём преимущества твоего решения (которого кстати я по-прежнему не увидел, даже наброска, а имею возможность лишь косвенно догадываться по кусочкам кода и брошенным фразам)? Ы?
Готового производительного решения и нет. А преимущества в отсутствии необходимости перехода на другой язык с сильно измененным синтаксисом только для возможности сопоставления с образцом.
Re[10]: Действительно ли ML языки хороши в компиляторописани
Здравствуйте, Mamut, Вы писали:
M>Приведенный вариант просто вернет true или false. Мой вариант вернет список не прошедших валидацию полей и всех ошибок, которые с ними связаны (правила валидации могут передаваться списком).
А, ну надо было тогда в условие задачи, добавить, что правила проверки должны задаваться более-менее декларативно.
M>Более того, проверка полей может отдаваться на растерзание внешним функциям.
Неужели думаете что в Java нельзя сообщить функции, что определенное действие она может проделать при помощи некоторой внешней функции, неизвестной на момент разработки?
M>А что, если полей станет больше? Как написать общую функцию, которая: M>- примет неограниченый список полей;
Думаете в Java функция не может принять неограниченный список параметров? M>- для кажого поля можно передать от 0 до бесконечного количества правил;
Думаете в Java не поддерживаются массивы с переменной длиной, от 0 до ...? M>- каждое правило позволяет отдать проверку внешней функции или устроить сравнение с переданым значением или другим полем;
Неужели думаете что в Java нельзя сообщить функции, что определенное действие она может проделать при помощи некоторой внешней функции, неизвестной на момент разработки? M>- возвращает осмысленный список полей, не пршедших валидацию, со списком ошибок для каждого поля?
M>При этом функция — достаточно общая, позволяет передавать 5 видов различных правил, причем по нескольку штук на поле.
M>Расширим пример. У нас появится пункт о лицензионном соглашении, еще несколько checkbox'ов, которые необходимо отметить, а также год рождени, который должен быть раньше 1980-го года:
M>Причем полученый список ошибок разбрать не просто легко, а очнь легко ПМ по [H|T] по списку и вперед
Сопоставление с образцом для списка — задача существенно более простая. Заметьте, в большинстве случаев когда рекламируют сопоставление с образцом, то показывают это на примере вариантов.
M>А функцию можно спокойно дописать для подержки более сложных правил. Например:
Неужели сомневаетесь, что на Java можно дописывать функции?
M>При этом функция остается общей и может использоваться для любых полей любой веб-формы. Без ПМа здесь только один выход — создание некоего движка правил. Но это — совсем неудобно
А у вас стало быть не движок правил?
public void main() {
Form form = new Form();
form.put("login", "name");
form.put("password", "123");
form.put("password_repeated", "123");
form.put("email", "name@domain.com");
form.put("born_year", "1980");
Object[] rules = new Object[] {
"login", "length", parameters(4, 16),
"password", "length", parameters(4, 16),
"password", "=", parameters("password_repeated"),
"email", "email", parameters(),
"born_year", "<", parameters("1990")
};
check(form, rules);
}
// О боже мой, кажется, я реализовал сопоставление с образцом для списка в Java!
// Этого не может быть, наверное я сплю.private void check(Form form, Object[] rules) {
for (int index = 0; index < rules.length; index += 3) {
String field = rules[index].toString();
String rule = rules[index+1].toString();
Object[] parameters = (Object[])rules[index+2];
check(form, field, rule, parameters);
}
}
private void check(Form form, String field, String rule, Object[] parameters) {
if (rule.equals("length")) {
int min = (Integer)parameters[0];
int max = (Integer)parameters[1];
checkLength(form.get(field), min, max);
}
...
// остальной код в той же мере тривиален
}
Re[5]: Действительно ли ML языки хороши в компиляторописани
z00n,
Z>Паттерн матчинг для луа я увидел в metalua, но он был реализован наивно(проверяем все подряд пока не false, goto следующая строка) — захотелось сделать лучше Z>ФВП — там уже есть, вместо continuations есть coroutines, вместо list comprehensions есть итераторы. Я добавил поддержку списков в стиле ML, что вкупе с паттерн-матчингом позволило мне воровать достаточно нетривиальные программы написанные на Haskell или Ocaml.
Спасибо за информацию, интересно. Как говорят в определённых кругах, "автор, пиши ещё"
Здравствуйте, z00n, Вы писали:
Z>У меня есть самопальный компилятор, который умеет переписывать патернматчинг как каскад вложенных if-else, правда таргетит язык с динамической типизацией(Lua) — что очевидным образом увеличивает количество тестов. Вообще, конечно, свитчи или goto лучше, но в луа goto есть только в байткоде, а мне хотелось сохранить минимальную читаемость. Использован модифицированный алгоритм Sestoft'а, как в MosML, MLKit, Nemerle.
Sestoft — это вот этот?