Функционально извлечения строк из базы данных в Scala


Рассмотрим следующий фрагмент кода:

var result = List[Measurement]()

val dbResult = conn.createStatement.executeQuery(unsentMeasurements)

while(dbResult.next) {
  result ::= Measurement(dbResult.getString("stuff"), dbResult.getString("morestuff"))
}

result.reverse

Против этого:

val dbResult = conn.createStatement.executeQuery(unsentMeasurements)

Stream.continually(dbResult).takeWhile(_.next) map { r =>
  Measurement(r.getString("stuff"), r.getString("morestuff"))
}

Любые недостатки в моем обосновании? Каких-либо улучшений?



Комментарии
1 ответ

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

trait Sql[A] { self =>
def unsafePerformIO(ds: javax.sql.DataSource): A = unsafePerformIO(ds.createConnection)

def unsafePerformIO(conn: java.sql.Connection): A //abstract

def map(f: A => B): Sql[B] = new Sql[B] {
def unsafePerformIO(conn: Connection) = f(self.unsafePerformIO(conn))
}
def flatMap(f: A => Sql[B]): Sql[B] = new Sql[B] {
def unsafePerformIO(conn: Connection)
= f(self.unsafePerformIO(conn)).unsafePerformIO(conn)
}

}

Я представляю, как вы можете сказать "да?" в этот момент. Позвольте представить вам товарища:

object Sql {
def apply[A](comp: Connection => A) = new Sql[A] {
def unsafePerformIO(conn: Connection) = comp(conn)
}
}

Идея (конечно) является то, что вы откладываете побочные эффекты до конца мира, инкапсуляция значений, возвращаемых базой данных в тип, который представляет собой значение, возвращаемое из вызова ДБ, таким же образом параметр представляет значение, которое не может существовать.

Использование становится чем-то вроде этого:

Sql { conn => process( conn.prepareStatement.executeQuery ) }

Где процесс способ может выглядеть ваш код (выше) возможно.

Я представляю, как это может поднять несколько вопросов:

Что? Да? Я имею в виду, что?

Ну, он позволяет сделать что-то вроде этого:

def getStuff1: Sql[Int]
def getStuff2: Sql[Int]
def useStuff(i: Int): Sql[String]

Затем вы можете составить, используя для выделения

val result = 
for {
i <- getStuff1
j <- getStuff2
s <- useStuff(i + j)
}
yield s

Каков тип этого выражения? Ну, это в SQL[строка] конечно! Чтобы заполучить эту строку:

result.unsafePerformIO(myDataSource)

Это создает единую связь, которая Затем продеваем через вычисления.

Чего не хватает? Ну, управление ресурсами (т. е. очистки соединения, конечно). Я понимаю, все это будет доступно в scalaz7

3
ответ дан 21 февраля 2012 в 08:02 Источник Поделиться