haskell - 协调镜头使用与数据库访问

标签 haskell haskell-lens lenses

我最近一直在玩镜头,发现它们的预期用途非常令人愉快——深入研究复杂的数据结构。但我最欣赏它们的领域之一是数据库访问(特别是 sqlite,但我认为我的问题适用于大多数数据库),但我看不出有任何方法可以编写不会严重牺牲的镜头性能或粒度。

如果我写一个镜头(或者我认为可能是一个棱镜,根据 NULLable 字段?)从数据库到表,从表到行,从行到列,每一步都会导致数据库访问,这意味着应该是一次访问至少是 4。

另一方面,如果我的目标是使用镜头/棱镜以 1:1 映射 DB 访问,那么当我只想查看哪些列是时,我会得到无法分解成更小的镜头的大镜头在表中,依此类推。

将镜头与数据库一起使用是否有意义,如果是这样,我是否错过了一种明显的方法来避免重复工作以避免不必要的数据库访问?

最佳答案

在我看来,您想以类似于 linq IQueryable 的方式使用 lens在 C# 中。

例如,如果您有以下类型:

data Project = Project {
  _projectId :: Int
  , _projectPriority :: Int
  , _projectName :: String
  , _projectTasks :: [Task]
   } deriving (Show)

data Task = Task {
  _taskId :: Int
  , _taskName :: String
  , _taskEstimate :: Int
  } deriving (Show)


makeLenses ''Project
makeLenses ''Task

和一个数据库:

create table projects ( id, name, priority);
create table tasks (id, name, estimate, projectId);

insert into projects values (1, 'proj', 1), (2, 'another proj', 2);

insert into tasks values (1, 'task1', 30, 1), (2, 'another', 40, 1),
                        (3, 'task3', 20, 2), (4, 'more', 80, 2);

如果您想从优先级大于 1 的项目中获取任务名称列表,那么如果您可以使用:
highPriorityTasks :: IO [String]
highPriorityTasks = db ^.. projects . filtered (\p -> p ^. projectPriority > 1 )
                    . projectTasks . traverse . taskName

并使用以下查询查询数据库:

select t.name from projects as p 
inner join tasks as t on t.projectId = p.id 
where p.priority > 1;

不幸的是,图书馆不可能做到这一点。基本上,为了提高数据库效率,您(通常)必须在一个查询中完成所有操作。这样做是 Not Acceptable :

select * from projects where priority > 1;
for each project:
   select name from tasks where projectId = <project>.id    

不幸的是,不可能分解函数来知道它们是由什么构成的。除了类型之外,如果不运行它,您将无法找到有关函数的任何信息。所以没有办法从 filtered 中提取数据。帮助构建查询的函数。也不可能从完整的表达式中提取子透镜。所以这是不可能使用镜头库的。

目前你能得到的最好的方法是使用一组函数查询数据库,并使用lens查询结果数据。看到这个blog post about yesod举个例子。

一个相关的问题是这是否可能。为此,我们需要为数字和字符串运算符创建一种子语言,以及跟踪所做工作的组合。这是可能的。例如,您可以构建一个 Num 类型来记录对其所做的一切:
data TrackedNum = TrackedNum :-: TrackedNum
                | TrackedNum :+: TrackedNum
                | TrackedNum :*: TrackedNum
                | Abs TrackedNum
                | Signum TrackedNum
                | Value Integer
  deriving (Show)

instance Num TrackedNum where
  a + b = a :+: b
  a * b = a :*: b
  a - b = a :-: b
  abs a = Abs a
  signum a = Signum a
  fromInteger = Value

t :: TrackedNum
t = 3 + 4 * 2 - abs (-34)

> t 
(Value 3 :+: (Value 4 :*: Value 2)) :-: Abs (Value 0 :-: Value 34)

对 bool 运算符(您将需要一个新的类型类)、列表运算符和函数组合(即 Category 类)重复该过程,您应该能够创建一个“白盒”函数,然后可以用于创建高效的 sql 查询。不过,这可不是一件小事!

关于haskell - 协调镜头使用与数据库访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24462070/

相关文章:

haskell - 在 Haskell 中使用 Lens 修改值

exception - 与 aeson/attoparsec 进行管道,一旦源没有更多数据,如何无异常地干净退出

algorithm - 在 Haskell 中实现 Karatsuba 算法

haskell - 为非记录数据类型编写或生成镜头

haskell - 在没有 Monoid 实例的情况下,如何处理 Control.Lens.Indexed 中 at 的 Maybe 结果

haskell - 使用透镜库组成部分 setter/getter

haskell - 使用镜头两次

haskell - 相当于mapM的箭头?

haskell - 是否可以有递归求和类型,每个 'level' 都有不同的值?

haskell - 镜头修改失败