0

给定以下持久类型:

share
    [mkPersist sqlSettings, mkMigrate "migrateAll"]
    [persistLowerCase|
Account
    email Text
    passphrase Text
    firstName Text
    lastName Text
    deriving Eq Show Generic
|]

我认为生成的是一种镜头,即AccountEmailAccountPassphrase。是否可以以这种方式组合这些,不一定是组合,而是说字符串连接,我经常发现自己在编写这些函数:

accountFullName :: SqlExpr (Entity Account) -> SqlExpr Text
accountFullName acc = acc ^. AccountFirstName ++. val " " ++. acc ^. AccountLastName

如果我能以类似的方式定义它会很好,Account*这样我就可以使用而不是使用原始函数来调用它们^.,即acc ^. AccountFullName. 这可能不是使用这些访问器的适当方式,但如果不是,我会很想知道为什么,因为我觉得这可能有助于我进一步理解 Persistent 库的这一部分,因为当我看的时候我感到相当迷茫在周围的代码EntityField...

4

1 回答 1

1

这真的不可能。

(^.) :: (PersistEntity val, PersistField typ)
     => expr (Entity val)
     -> EntityField val typ
     -> expr (Value typ)

您会看到第二个参数是EntityField val typ,它是在类中定义的类型族PersistEntity val。这是为您的表预定义的,并且无法更改,因此此特定运算符不能以您想要的方式用于自定义访问器。


当您使用persistLowerCase和朋友时,它使用 Template 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
  ...

(这在语法上并不准确,并且缺少很多细节,但我认为它为这种情况提供了足够的上下文。)

因此,您传递给的“镜头”(^.)实际上只是与您的表类型关联的类型的构造函数Account。您无法创建新的构造函数或动态重新关联类型族,因此您无法制作可以传递给(^.). 这些访问器实际上是一成不变的。


我认为使用原始函数是最有意义的。accountFullName acc写起来还不错,它清楚地表明你正在做一些比仅仅拉取字段值更复杂的事情。

于 2018-08-02T19:25:40.783 回答