代码之家  ›  专栏  ›  技术社区  ›  danbroooks

有没有可能定义你自己的Persistent/Esqueleto镜头?

  •  0
  • danbroooks  · 技术社区  · 8 年前

    给定以下持久类型:

    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 . 这可能不是使用这些访问器的合适方式,但如果不是,我会有兴趣知道为什么,因为我觉得这可能有助于进一步理解持久库的这一部分,因为当我查看周围的代码时,我感到相当迷茫 EntityField ...

    1 回复  |  直到 8 年前
        1
  •  1
  •   DarthFennec    8 年前

    这是不可能的。

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

    你会发现第二个论点是 EntityField val typ ,它是在 PersistEntity val 上课。这是为您的表预定义的,并且不能更改,因此此特定运算符不能按您所需的方式用于自定义访问器。


    当你使用 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
      ...
    

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

    所以你传递给的“镜头” (^.) 实际上只是与表类型关联的类型的构造函数 Account . 不能创建新的构造函数或动态地重新关联类型族,因此不能生成可以传递给 (^.) . 这些存取器实际上是用石头固定的。


    我认为使用原始函数是最有意义的。 accountFullName acc 写起来也不错,而且很明显你做的事情比仅仅提取字段值要复杂一些。

    推荐文章