Comments 8
Про идеологические недостатки решения: было бы справедливо отметить что:
Использование исключений в данном случае лежит полностью на совести автора. При желании можно было бы организовать монаду и тогда было бы все "по функциональным канонам". Другое дело не понятно какой от этого выигрыш.
- Ленивость вычислений в конкретно этой ситуации зависит не от языка, а от реализации. К текущему коду можно добавить небольшой кусок и выражения станут вычисляться лениво.
Язык арифметических выражений придуман не очень хороший, отсюда "прагматические недостатки" 2-4.
Синтаксисы let и where как-то дублируют друг друга.
Кажется, из-за вложенности классов допустимо что-то вроде Result.Some.Some.Error("...") (не уверен). Это было бы странно для DSL.
Без приоритета операций совсем никуда — нехорошо, если выражение 2 + 1 * 3 внезапно равно 9.
Нагляднее было бы представлять выражения в виде деревьев, тогда могло бы появиться "описание грамматики" (в виде case-классов, как и у вас), приоритет операций стал бы не нужен, и наверно можно было бы ввести какую-то типизацию.
val expr = If(
Less(
Var("x"),
Const(3)),
Mul(Var("y"),Const(4)),
Mul(
Const(5),
Add(
Const(1),
Const(2)))) where (
"x" eq 43,
"y" eq 3)
Здесь синтаксический сахар языка Scala использован не в полной мере, т.к. я не знаю, что из сахара есть в Kotlin. Это — минималистичный по использованию сахара вариант. Можно писать и короче, типа Mul (var"x", var"y", 5, 6)
Про
Result.Some.Some.Error("...")
sealed класс транслируются в abstact class, а каждый наследник в inner class(в скале ведь так-же case классы работают). По этому описанной ситуации быть не может.
Просто захотелось иметь возможность описывать связывания до и после выражения. Вы правы, семантика одна.
2 + 1 * 3 будет вычислено еще на этапе записи выражения в рантайме, так-как это перегруженные операторы.
Уже сейчас есть возможность описывать выражения через деревья с помощью конструкторов). Могу написать эквивалент вашему. Инфиксные функции я добавил для демонстрации возможностей dsl. Согласен, что с ними я получил кучу проблем.
Написал If Else, почти как у вас и функции к нему:
val example=
ifE(const(1), BoolKey.Less, const(2)) {
variable("helloHabr")
} ifFalse {
const(2016.0)
} where ("helloHabr" to 2016)
Теперь точно нужна проверка типов. Легко можно разные типы в then else вернуть.
Проверку типов реализовать несложно:
abstract sealed class Expr[+T]
object EmptyExpr extends Expr[Unit]
case class If[+R,R1:R,R2:R](test: Expr[Boolean],ifTrue: Expr[R],ifFalse: Expr[R] = EmptyExpr) extends Expr[R]
Язык арифметических выражений с конструкцией let как dsl на Kotlin