是否可以定义您自己的 Persistent / Esqueleto 镜头?
Is it possible to define your own Persistent / Esqueleto lens?
给定以下持久类型:
share
[mkPersist sqlSettings, mkMigrate "migrateAll"]
[persistLowerCase|
Account
email Text
passphrase Text
firstName Text
lastName Text
deriving Eq Show Generic
|]
我认为生成的是一种镜头,即AccountEmail
、AccountPassphrase
等。是否可以以这种方式组合这些,不一定是组合,而是说字符串连接,我经常发现自己编写了这些类型的函数:
accountFullName :: SqlExpr (Entity Account) -> SqlExpr Text
accountFullName acc = acc ^. AccountFirstName ++. val " " ++. acc ^. AccountLastName
如果我能以与Account*
类似的方式定义它就好了,这样我就可以使用^.
调用它们而不是使用原始函数,即acc ^. AccountFullName
。这可能不是使用这些访问器的合适方式,但如果不是,我很想知道为什么,因为我觉得这可能有助于进一步理解 Persistent 库的这一部分,因为当我看的时候我感到很迷茫在 EntityField
...
附近的代码处
这真的不可能。
(^.) :: (PersistEntity val, PersistField typ)
=> expr (Entity val)
-> EntityField val typ
-> expr (Value typ)
您会看到第二个参数是 EntityField val typ
,它是 PersistEntity val
class 中定义的类型族。这是为您的 table 预定义的,无法更改,因此此特定运算符不能按您希望的方式用于自定义访问器。
当您使用 persistLowerCase
和朋友时,它使用模板 Haskell 来解析您的定义并生成适当的数据定义。据我了解,生成了类似于以下内容的内容:
data Account = Account
{ accountEmail :: Text
, accountPassphrase :: Text
, accountFirstName :: Text
, accountLastName :: Text
}
data AccountField a where
AccountId :: AccountField Key
AccountEmail :: AccountField Text
AccountPassphrase :: AccountField Text
AccountFirstName :: AccountField Text
AccountLastName :: AccountField Text
instance PersistEntity Account where
data EntityField Account a = AccountField a
...
(这在语法上不准确,缺少很多细节,但我认为它为这种情况提供了足够的上下文。)
因此,您传递给 (^.)
的 "lens" 实际上只是与您的 table 类型 Account
关联的类型的构造函数。您不能创建新的构造函数或动态地重新关联类型族,因此您不能创建可以传递给 (^.)
的其他内容。这些访问器实际上是一成不变的。
我认为只使用原始函数最有意义。 accountFullName acc
写起来还不错,它清楚地表明您正在做的事情比仅仅提取字段值要复杂一些。
给定以下持久类型:
share
[mkPersist sqlSettings, mkMigrate "migrateAll"]
[persistLowerCase|
Account
email Text
passphrase Text
firstName Text
lastName Text
deriving Eq Show Generic
|]
我认为生成的是一种镜头,即AccountEmail
、AccountPassphrase
等。是否可以以这种方式组合这些,不一定是组合,而是说字符串连接,我经常发现自己编写了这些类型的函数:
accountFullName :: SqlExpr (Entity Account) -> SqlExpr Text
accountFullName acc = acc ^. AccountFirstName ++. val " " ++. acc ^. AccountLastName
如果我能以与Account*
类似的方式定义它就好了,这样我就可以使用^.
调用它们而不是使用原始函数,即acc ^. AccountFullName
。这可能不是使用这些访问器的合适方式,但如果不是,我很想知道为什么,因为我觉得这可能有助于进一步理解 Persistent 库的这一部分,因为当我看的时候我感到很迷茫在 EntityField
...
这真的不可能。
(^.) :: (PersistEntity val, PersistField typ)
=> expr (Entity val)
-> EntityField val typ
-> expr (Value typ)
您会看到第二个参数是 EntityField val typ
,它是 PersistEntity val
class 中定义的类型族。这是为您的 table 预定义的,无法更改,因此此特定运算符不能按您希望的方式用于自定义访问器。
当您使用 persistLowerCase
和朋友时,它使用模板 Haskell 来解析您的定义并生成适当的数据定义。据我了解,生成了类似于以下内容的内容:
data Account = Account
{ accountEmail :: Text
, accountPassphrase :: Text
, accountFirstName :: Text
, accountLastName :: Text
}
data AccountField a where
AccountId :: AccountField Key
AccountEmail :: AccountField Text
AccountPassphrase :: AccountField Text
AccountFirstName :: AccountField Text
AccountLastName :: AccountField Text
instance PersistEntity Account where
data EntityField Account a = AccountField a
...
(这在语法上不准确,缺少很多细节,但我认为它为这种情况提供了足够的上下文。)
因此,您传递给 (^.)
的 "lens" 实际上只是与您的 table 类型 Account
关联的类型的构造函数。您不能创建新的构造函数或动态地重新关联类型族,因此您不能创建可以传递给 (^.)
的其他内容。这些访问器实际上是一成不变的。
我认为只使用原始函数最有意义。 accountFullName acc
写起来还不错,它清楚地表明您正在做的事情比仅仅提取字段值要复杂一些。