如何在Haskell中连接幻象类型中的元组?

如何在Haskell中连接幻象类型中的元组?,haskell,hlist,Haskell,Hlist,我正在编写一个SQL组合器,它允许SQL片段组成一个幺半群。我大致有这样一种类型这是一个简化的实现: data SQLFragment = { selects :: [String], froms :[String], wheres :: [String]} instance Monoid SQL Fragment where ... 这使我能够轻松组合我经常使用的SQL,并执行以下操作: email = select "email" <> from "user" name

我正在编写一个SQL组合器,它允许SQL片段组成一个幺半群。我大致有这样一种类型这是一个简化的实现:

data SQLFragment = { selects :: [String], froms :[String], wheres :: [String]}

instance Monoid SQL Fragment where ...
这使我能够轻松组合我经常使用的SQL,并执行以下操作:

email = select "email" <> from "user" 
name  = select "name" <> from "user"
administrators = from "user" <> where_ "isAdmin = 1"

toSql $ email <> name <> administrators
=> "SELECT email, name FROM user WHERE isAdmin = 1"
这就是为什么我需要

 rows :: [(String, String)]
为了避免手动添加此显式且无用的类型签名,我有以下想法: 我向SQLFragment添加了一个幻影类型,并使用它强制查询函数的类型。所以我可以有这样的东西

email = select "email" <> from "user" :: SQLFragment String
name  = select "name" <> from "user" :: SQLFragment String
administrators = from "user" <> where_ "isAdmin = 1" :: SQLFragment ()
我的第一个问题是我不能再使用了,因为SQLFragment a不再是幺半群了。 第二个问题是如何实现我的新方法来正确计算幻影类型

我找到了一种我认为很难看的方法,我希望有更好的解决办法。 我创建了一个类型化版本的SQLFragment,并使用phantom属性,它是一个HList

然后我创建一个新的类型化操作符:!!我不理解类型签名,所以我不写它

(TQuery q e) !<>! (TQuery q' e') = TQuery (q<>q') (e.*.e')
并为一些元组实例化它

另一种解决方案可能是使用类型族并手动写入每个元组组合

type instance Result (HList '[a])  = (SQL.Only a)
type instance Result (HList '[HList '[a], b])  = (a, b)
type instance Result (HList '[HList '[HList '[a], b], c])  = (a, b, c)
type instance Result (HList '[HList '[HList '[HList '[a], b], c], d])  = (a, b, c, d)
type instance Result (HList '[HList '[HList '[HList '[HList '[a], b], c], d], e])  = (a, b, c,d, e)
等等

这就行了。我可以使用结果族编写函数

我的主要程序如下所示:

email = TQuery (select "email" <> from "user") ((undefined :: String ) .*. HNil)
name  = TQuery (select "name" <> from "user" ) ((undefined :: String ) .*. HNil)
administrators = TQuery (from "user" <> where_ "isAdmin = 1") (HNil)

main = do
       conn <- SQL.connect connectInfo
       rows <- execute conn $ email !<>! name !<>! administrators
       forM_ rows print
而且它有效

然而,有没有更好的方法来做到这一点,特别是在不使用HList的情况下,如果可能的话,尽量减少扩展

如果我以某种方式隐藏了幻影类型,这样我就可以有一个真正的幺半群,并能够使用而不是!!有没有办法恢复类型?

考虑使用which解决类型化数据库查询问题。Haskell数据库中的记录工作正常,但它们不提供很多操作,并且类型更长,因为它们不使用-XDataKinds

我对您当前的代码有一些建议:

newtype TQuery (e :: [*]) = TQuery SQLFragment
更好,因为e实际上是幻影类型。然后,您的追加操作可以如下所示:

(!<>!) :: TQuery a -> TQuery b -> TQuery (HAppendR a b)
TQuery a !<>! TQuery b = TQuery (a <> b)
如果您想继续使用haskelldb的HList+mysql简单和重复部分,那么一个实例QueryResults记录r可能是合适的。一个未发布的解决了一个非常复杂的问题
类似的问题,可能值得一看。

编写辅助函数strQuery::Connection->Query->IO[String,String]有什么问题;strQuery=SQL.query,很像编写函数readInt::String->Int;readInt=read?如果您总是得到相同的返回类型,或者只是少数几种类型中的一种,那么这种方法应该是非常易于管理的,并且不需要任何花哨的管道来工作。否则,我认为必须在TQuery q e中指定inline.in类型没有问题!!TQuery q'e'=TQuery qq e.*。也许你的意思是qq'?@didierc:是的,当然。我修好了。谢谢。@bheklir:添加类型签名的错误在于它是多余的,理论上是不必要的,每次我修改查询时,如果我需要在两个多余的位置修改某些内容,我就需要修改类型签名。此外,任何类型签名都会进行类型检查,但在运行时可能会失败。在我的plumbery中,一旦正确地声明了一个列(只执行一次)并且我从模式生成了该列,类型检查将工作的每个查询组合。所以它基本上更安全。@didierc:Hoogle不起作用,但Hayoo发现了它..*。来自HList,它是的异构版本:。在HList前加上一些东西。这确实更干净,也正是我想要的,尽管我更愿意完全删除HList。我一直在考虑HaskellDB和Esqueletto,但它们都不是我想要做的,基本上不需要指定from子句和自动连接。
type instance Result (HList '[a])  = (SQL.Only a)
type instance Result (HList '[HList '[a], b])  = (a, b)
type instance Result (HList '[HList '[HList '[a], b], c])  = (a, b, c)
type instance Result (HList '[HList '[HList '[HList '[a], b], c], d])  = (a, b, c, d)
type instance Result (HList '[HList '[HList '[HList '[HList '[a], b], c], d], e])  = (a, b, c,d, e)
execute :: (SQL.QueryResults (Result e)) => 
        SQL.Connection -> TQuery e -> SQL.Connection -> IO [Result e]
execute conn (TQuery q _ ) = SQL.query_ conn (toSql q)
email = TQuery (select "email" <> from "user") ((undefined :: String ) .*. HNil)
name  = TQuery (select "name" <> from "user" ) ((undefined :: String ) .*. HNil)
administrators = TQuery (from "user" <> where_ "isAdmin = 1") (HNil)

main = do
       conn <- SQL.connect connectInfo
       rows <- execute conn $ email !<>! name !<>! administrators
       forM_ rows print
newtype TQuery (e :: [*]) = TQuery SQLFragment
(!<>!) :: TQuery a -> TQuery b -> TQuery (HAppendR a b)
TQuery a !<>! TQuery b = TQuery (a <> b)
type family Result (a :: [*])
type instance Result '[a])  = (SQL.Only a)
type instance Result '[a, b]  = (a, b)
type instance Result '[a, b, c]  = (a, b, c)
type instance Result '[a, b, c, d]  = (a, b, c, d)
type instance Result '[a, b, c, d, e]  = (a, b, c,d, e)
-- so you might match the 10-tuple mysql-simple offers