Поддержка SQL в стиле коллекции Scala, как в LINQ - PullRequest
11 голосов
/ 06 декабря 2010

Насколько я понимаю, единственное, что поддерживает LINQ, чего в настоящее время нет в Scala с библиотекой коллекций, - это интеграция с базой данных SQL.

Насколько я понимаю, LINQ может "накапливать" различныеопераций и может выдавать «целое» утверждение в базу данных при запросе на его обработку там, предотвращая простое SELECT первое копирование всей таблицы в структуры данных виртуальной машины.

Если я ошибаюсь,Я был бы счастлив, чтобы меня исправили.

Если нет, то что необходимо для поддержки того же в Scala?

Не было бы возможно написать библиотеку, которая реализует интерфейс коллекции, но неУ вас есть какие-либо структуры данных, поддерживающие его, кроме строки, которая собирается с последующей коллекцией в требуемом операторе базы данных?

Или я совершенно не прав в своих наблюдениях?

Ответы [ 5 ]

13 голосов
/ 07 декабря 2010

Следует отметить, что в Scala есть экспериментальная поддержка деревьев выражений. Если вы передадите анонимную функцию в качестве аргумента методу, ожидающему параметр типа scala.reflect.Code[A], вы получите AST.

scala> import scala.reflect.Code      
import scala.reflect.Code 
scala> def codeOf[A](code: Code[A]) = code
codeOf: [A](code:scala.reflect.Code[A])scala.reflect.Code[A]
scala> codeOf((x: Int) => x * x).tree 
res8: scala.reflect.Tree=Function(List(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),Apply(Select(Ident(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),Method(scala.Int.$times,MethodType(List(LocalValue(NoSymbol,x$1,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))),PrefixedType(ThisType(Class(scala)),Class(scala.Int))))),List(Ident(LocalValue(NoSymbol,x,PrefixedType(ThisType(Class(scala)),Class(scala.Int)))))))

Это использовалось в библиотеке генерации байт-кода 'Mnemonics', которая была представлена ​​ ее автором Йоханнесом Рудольфом на Scala Days 2010.

13 голосов
/ 07 декабря 2010

Как автор ScalaQuery, мне нечего добавить к объяснению Стилгара.Часть LINQ, которая отсутствует в Scala, действительно является деревьями выражений.Именно поэтому ScalaQuery выполняет все свои вычисления для типов Column и Table вместо базовых типов этих объектов.

Вы объявляете таблицу как объект Table с проекцией (кортежем) ее столбцов, например:

class User extends Table[(Int, String)] {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def * = id ~ name
}

User.id и User.name теперь имеют тип Column [Int] и Column [String] соответственно.Все вычисления выполняются в монаде Query (которая является более естественным представлением запросов к базе данных, чем операторы SQL, которые должны быть созданы из нее).Возьмите следующий запрос:

val q = for(u <- User if u.id < 5) yield u.name

После некоторых неявных преобразований и отладки это переводится в:

val q:Query[String] =
  Query[User.type](User).filter(u => u.id < ConstColumn[Int](5)).map(u => u.name)

Методы filter и map не должны проверять свои аргументы как деревья выражений в порядкечтобы построить запрос, они просто запускают их.Как видно из типов, то, что внешне выглядит как «u.id:Int <5: Int», на самом деле является «u.id:Column[Int] <u.id:Column[Int]».Запуск этого выражения приводит к запросу AST, подобному Operator.Relational ("<", NamedColumn ("user", "id"), ConstColumn (5)).Точно так же методы «filter» и «map» монады Query фактически не выполняют фильтрацию и отображение, а вместо этого создают AST, описывающий эти операции. </p>

Затем QueryBuilder использует этот AST для конструирования фактического SQLоператор для базы данных (с синтаксисом, специфичным для СУБД).

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

5 голосов
/ 06 декабря 2010

С LINQ компилятор проверяет, скомпилировано ли лямбда-выражение в IEnumerable или в IQueryable. Первые работы, как коллекции Scala. Второй компилирует выражение в дерево выражений (то есть структуру данных). Сила LINQ заключается в том, что сам компилятор может переводить лямбда-выражения в деревья выражений. Вы можете написать библиотеку, которая создает деревья выражений с интерфейсом, похожим на тот, который у вас есть для коллекции, но как вы собираетесь заставить компилятор строить структуры данных (вместо кода JVM) из лямбд?

При этом я не уверен, что Scala предоставляет в этом отношении. Возможно, в Scala можно построить структуры данных из лямбд, но в любом случае я считаю, что вам нужна аналогичная функция в компиляторе для обеспечения поддержки баз данных. Помните, что базы данных - не единственный источник данных, для которого вы можете создавать провайдеров. Существует множество поставщиков LINQ, например Active Directory или Ebay API.

Edit: Почему не может быть только API?

Для выполнения запросов вы не только используете методы API (фильтр, где и т. Д.), Но также используете лямбда-выражения в качестве аргументов этих методов. Где (x => x> 3) (C # LINQ ). Компиляторы переводят лямбда-выражения в байт-код. API должен создавать структуры данных (деревья выражений), чтобы вы могли преобразовать структуру данных в базовый источник данных. По сути, вам нужен компилятор, чтобы сделать это за вас.

Отказ от ответственности 1: Может быть (просто возможно) есть какой-то способ создания прокси-объектов, которые выполняют лямбда-выражения, но перегружают операторы для создания структур данных. Это приведет к несколько худшей производительности, чем фактическое LINQ (время выполнения или время компиляции). Я не уверен, возможна ли такая библиотека. Возможно, библиотека ScalaQuery использует аналогичный подход.

Отказ от ответственности 2: Возможно, язык Scala на самом деле может предоставлять лямбды в качестве проверяемых объектов, чтобы вы могли получить дерево выражений. Это сделало бы лямбда-функцию в Scala эквивалентной функции в C #. Возможно, библиотека ScalaQuery использует эту гипотетическую функцию.

Редактировать 2: Я немного покопался. Похоже, что ScalaQuery использует библиотечный подход и перегружает группу операторов для создания деревьев во время выполнения. Я не совсем уверен в деталях, потому что я не знаком с терминологией Scala и с трудом читаю сложный код Scala в статье: http://szeiger.de/blog/2008/12/21/a-type-safe-database-query-dsl-for-scala/

Как и любой объект, который может использоваться или возвращаться в запросе, таблица параметризована с типом значений, которые она представляет. Это всегда кортеж отдельных типов столбцов, в нашем случае Integer и Strings (обратите внимание на использование java.lang.Integer вместо Int; подробнее об этом позже). В этом отношении SQuery (как я его сейчас назвал) ближе к HaskellDB, чем к LINQ, потому что Scala (как и большинство языков) не дает вам доступа к AST выражения во время выполнения. В LINQ вы можете писать запросы, используя реальные типы значений и столбцов в вашей базе данных, и преобразовывать AST выражения запроса в SQL во время выполнения. Без этой опции мы должны использовать мета-объекты, такие как Table и Column, чтобы построить из них наш собственный AST.

Очень классная библиотека. Я надеюсь, что в будущем он получит любовь, которую заслуживает, и станет инструментом, готовым к реальному производству.

4 голосов
/ 06 декабря 2010

Возможно, вы хотите что-то вроде http://scalaquery.org/. Это именно то, что предлагает ответ @Stilgar, за исключением того, что это только SQL.

1 голос
/ 07 декабря 2010

проверить http://squeryl.org/

...