我最近一直在玩镜头,发现他们非常愉快的预期用途 – 挖掘复杂的数据结构.但是我最感激的一个领域是数据库访问(特别是sqlite,但是我认为我的问题归结于大多数DB),但是我看不到任何方式来写入不会牺牲的镜头性能或粒度.
如果我从DB到表,从表到行,从一行到一列,写一个镜头(或者我认为可能是一个可视的NULL可视字段?),那么每个步骤都会导致数据库访问意味着什么应该是一个访问是至少4.
另一方面,如果我的目标是将数据库访问1:1与镜头/棱镜的使用映射到一起,那么我将会看到大部分镜头不能被分解成更小的部分,当我想要看到什么列是在桌子里,等等.
解决方法
它听起来像我想要使用镜像在类似于
linq IQueryable在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,estimate,projectId); insert into projects values (1,'proj',1),(2,'another proj',2); insert into tasks values (1,'task1',30,'another',40,(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;
不幸的是,这是不可能的图书馆.基本上,为了有效的数据库明智,您(通常)必须在一个查询中执行任何操作.这样做是不可接受的:
select * from projects where priority > 1; for each project: select name from tasks where projectId = <project>.id
不幸的是,不可能分解函数来知道它们是什么.除了这种类型,你不能在没有运行它的情况下找到任何关于功能的东西.所以没有办法从过滤的函数中提取数据来帮助构建查询.也不可能从完整的表达中提取子透镜.所以这是不可能使用镜头库.
您目前可以获得的最好的方法是使用一组函数查询数据库,并使用镜头查询生成的数据.参见这个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)
对布尔运算符重复此过程(您将需要一个新类型类),列表运算符和函数组合(即Category类),您应该可以创建一个“白盒”函数,然后可以用来创建一个高效的SQL查询.这不是一个微不足道的事情!