【问题标题】:Haskell + Yesod: Separating insert logic using Persistent won't compile. Cannot figure out typesHaskell + Yesod:使用 Persistent 分离插入逻辑不会编译。无法确定类型
【发布时间】:2013-08-07 13:25:43
【问题描述】:

我是 Haskell 的新手,来自 Scala。我喜欢 Haskell,但我觉得在使用 persistent 时我正在与类型系统作斗争。

我的请求:我想将一些插入逻辑分离到它自己的方法中。我无法完全弄清楚类型或正确的方法。我所有失败的尝试都不会编译。下面是更简洁的问题。

这里是数据声明:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Curator
  name String
  url String
  feed String
  UniqueUrl url
  deriving Show
Article
  url String
  title String
  content String
  curatorId CuratorId Eq
  deriving Show
|]

这是一个失败的尝试:

insertArticle :: String -> String -> String -> MaybeT (???)
insertArticle url title content = do
    curatorId <- selectFirst [curatorName ==. "Fake Name"]
    lift $ do
        curator <- curatorId
        insert (Article url title content curator)

所以,我的问题:

  1. ??? 中应该是什么类型?
  2. lift 在正确的位置吗? (通常编译器更有帮助)。
  3. 有没有更好的方法来做到这一点?

PS - 我已经成功抽象出其他逻辑,例如插入只是给我带来了一个痛苦的世界。使用SqlPersistM时无法编译它

getFeeds :: SqlPersistM [Curator]
getFeeds = do
  curatorIds <- selectList [] [Asc CuratorName]
  let curatorGenerics = map entityVal curatorIds
  let curators = map (\x -> x :: Curator) curatorGenerics
  return curators

【问题讨论】:

    标签: haskell yesod


    【解决方案1】:

    insertArticle 的返回类型应该是SqlPersistM (Maybe ArticleId),因为它返回Just 一个插入的文章id 或NothingSqlPersistM monad 中。

    你可以实现类似的功能:

    insertArticle :: String -> String -> String -> SqlPersistM (Maybe ArticleId)
    insertArticle url title content = do
        curatorEntity <- selectFirst [CuratorName ==. "Fake Name"] []
        for curatorEntity $ \(Entity curatorId _) ->
            insert (Article url title content curatorId)
    

    我使用来自Data.Traversablefor 来处理MaybeselectFirst 在这里返回。

    但是,实际上,我不喜欢这种类型签名,因为它坚持 sql 后端。为了使其更通用,你可以像这样写一个类型注解。

    insertArticle :: (Applicative m, PersistQuery m, PersistMonadBackend m ~ PersistEntityBackend Curator) =>
                     String -> String -> String -> m (Maybe ArticleId)
    

    签名有点复杂,但这个函数适用于任何后端。

    顺便说一下,你的getFeeds可以简化一下。

    getFeeds :: (Functor m, PersistQuery m, PersistMonadBackend m ~ PersistEntityBackend Curator) =>
                m [Curator]
    getFeeds = map entityVal <$> selectList [] [Asc CuratorName]
    

    【讨论】:

    • 感谢您的快速回复!您是如何找到SqlPersistT 的类型的?每当我冒险使用更通用的insert 类型时,我都会遇到令人痛苦、难以理解的类型问题。我仍然习惯于浏览hackage,所以也许我错过了SqlPersistT。 PS - 感谢对getFeeds的改进!
    • 追问:你知道我在哪里可以阅读更多关于类型签名中的波浪号 (~) 的信息吗?那是说PersistMonadBackend mPersistEntityBackend Curator 的某种等价物或家族吗?
    • @weirdcanada: a ~ b 是一个类型相等约束,这意味着ab 的类型必须相同。文档:haskell.org/ghc/docs/7.4.1/html/users_guide/…
    • getFeeds为例。我们通常在处理程序中使用runDB 运行这个动作,所以getFeeds 的具体类型应该是YesodDB App [Curator]。如果你看一下instance YesodPersist App 的定义,你会发现它被扩展为SqlPersistT (HandlerT App IO) [Curator]。这意味着此操作将在 SqlPersistT m monad 中运行。
    • @snak 谢谢。我在 yesod 网络应用程序的上下文之外使用它,所以这是有道理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多