3

我对 Yesod 完全陌生(并且在 haskell 方面不是很有经验),我正在尝试构建我的第一个处理程序。我使用默认参数对我的应用程序进行了脚手架(我使用的是 Yesod 0.9.4.1 版本并在脚手架中选择 postgresql),现在我正在尝试使用 selectList 从表中检索一些数据。我在模型配置文件中定义了一个新表(我们称之为 Foo):

    Foo
        xStart Int
        yStart Int

并且想要传递 FooId 和其他一些 Foo 属性的列表,所以我定义了一个路由:

/foos/#Int/#Int/*FooId FoosReturnR GET

和一个处理程序:

    module Handler.FoosReturn where

    import Import

    selectWindowSize :: Int 
    selectWindowSize = 10000

    getFoosReturnR :: Int -> Int -> [FooId] -> Handler RepPlain
    getFoosReturnR x y withoutIds = do
        foos <- runDB $ selectList [FooId /<-. withoutIds, 
               FooXStart <. x + selectWindowSize,
               FooXStart >=. x - selectWindowSize, 
               FooYStart <. y + selectWindowSize,
               FooYStart >=. y - selectWindowSize] [] 
        return $ RepPlain $ toContent $ show foos

我在 Application.hs 中导入了处理程序并将其添加到 cabal 文件中,现在当我尝试运行它时,我收到一条错误消息,指出 FooId 不是 MultiPiece 的实例 - 但是当我尝试使其成为实例时,有一个错误说 FooId 是类型同义词并且不能是 MultiPiece 的实例 - 如何解决此问题?


编辑:丹尼尔:嗯,实际上我不知道 FooId 到底是什么——它是 Yesod 魔法的一部分,到目前为止我还不完全理解——它是从表定义中自动生成的——但它是某种数字。

因为我不知道如何使用 MultiPiece 我切换到更简单的解决方案并修改:

路线:/foos/#Int/#Int/#String FoosReturnR GET

处理程序:[还添加了一些日志记录]

    module Handler.FoosReturn where

    import Import
    import Data.List.Split
    import qualified Data.Text.Lazy as TL

    selectWindowSize :: Int 
    selectWindowSize = 10000

    getFoosReturnR :: Int -> Int -> String -> Handler RepPlain
    getFoosReturnR x y withoutIds = do
        app <- getYesod
        liftIO $ logLazyText (getLogger app) ("getFoosReturnR('" `TL.append` (TL.pack $ (show x) ++ "', '" ++ (show y) ++ "', '" ++ withoutIds ++ "') "))
        foos <- runDB $ selectList [FooId /<-. (map (\a -> read a :: FooId) $ splitOn "," withoutIds), 
               FooXStart <. x + selectWindowSize,
               FooXStart >=. x - selectWindowSize, 
               FooYStart <. y + selectWindowSize,
               FooYStart >=. y - selectWindowSize] [] 
        return $ RepPlain $ toContent $ show foos

现在它正在编译但是当我浏览到:http://localhost:3000/sectors/1/1/1,2我得到一个页面只包含: Internal Server Error Prelude.read: no parse

好吧,我不完全理解这里的 FooId 是什么 - 如何从包含数字的字符串列表中创建这样的 FooId 列表?

当然,最需要如何使 FooId 成为 MultiPiece 实例的解决方案。


编辑:

Daniel 和 svachalek,感谢您的帖子 - 我尝试了您的(Daniel 的)解决方案,但后来我收到错误说 [FooId] 是预期的(如在处理程序函数声明中)但是 FooId 类型被给出,这导致我下面的解决方案:

    data FooIds = FooIds [FooId] deriving (Show, Read, Eq)

    instance MultiPiece FooIds where
        toMultiPiece (FooIds fooList) = map (Data.Text.pack . show) fooList
        fromMultiPiece texts = 
            if length (filter isNothing listOfMaybeFooId) > 0
                then Nothing
                else Just $ FooIds $ map fromJust listOfMaybeFooId
            where 
                listOfMaybeFooId = map constructMaybeFooId texts
                constructMaybeFooId :: Text -> Maybe FooId
                constructMaybeFooId x = case reads (Data.Text.unpack x) :: [(FooId,String)] of
                        [(foo,_)] -> Just foo
                        _         -> Nothing

当然,我将路线更改为:/foos/#Int/#Int/*FooIds FoosReturnR GET

和处理程序:

    getFoosReturnR :: Int -> Int -> FooIds -> Handler RepPlain
    getFoosReturnR coordX coordY (FooIds withoutIds) = do

现在我在编译和运行时都没有遇到任何错误,唯一不满意的是我总是收到 Not Found 结果,即使我提供了应该给我一些结果的参数 - 所以现在我必须弄清楚如何确定准确发送到数据库的 SQL


编辑:

现在我看到“未找到”与问题有关,并且上述编辑不是解决方案 - 当我浏览到 localhost:3000/foos/4930000/3360000 然后我得到结果(但后来 FooIds 是空的) - 但是当我添加如下内容时:localhost:3000/sectors/4930000/3360000/1 然后我总是得到“未找到” - 所以它仍然无法正常工作..

4

3 回答 3

1

问题已经解决了:)

您可以从问题的最后一个编辑中使用我的实现并浏览到 URL,例如:http://localhost:3000/foos/4930000/3360000/Key {unKey = PersistInt64 3}/Key {unKey = PersistInt64 4} Key 类型派生 Read 但不是以非常友好(和预期)的方式:)

或者将 fromMultiPiece 的实现更改为:

    instance MultiPiece FooIds where
        toMultiPiece (FooIds fooList) = map (Data.Text.pack . show) fooList
        fromMultiPiece texts = 
            if length (filter isNothing listOfMaybeFooId) > 0
                then Nothing
                else Just $ FooIds $ map fromJust listOfMaybeFooId
            where 
                listOfMaybeFooId = map constructMaybeFooId texts
                constructMaybeFooId :: Text -> Maybe FooId
                constructMaybeFooId x = case TR.decimal x of 
                        Left err -> Nothing 
                        Right (el,_) -> Just $ Key (PersistInt64 el)

并使用如下 URL:http://localhost:3000/foos/4930000/3360000/1/2

非常感谢 Yesod Web Framework Google Group 的 David McBride


编辑:上述解决方案只有一个缺点 - 使用 PersistInt64 类型 - 使用这样的实现细节不是一个好习惯,但可以通过使用fromPersistValuetoPersistValue函数来修复它Database.Persist,如下所示:

    instance MultiPiece FooIds where
        toMultiPiece (FooIds fooList) = map (persistValuetoText . unKey) fooList
            where
                persistValuetoText x = case fromPersistValue x of
                    Left _ -> Data.Text.pack "" 
                    Right val -> Data.Text.pack $ show (val::Int) 
        fromMultiPiece texts = 
            if length (filter isNothing listOfMaybeFooId) > 0
                then Nothing
                else Just $ FooIds $ map fromJust listOfMaybeFooId
            where 
                listOfMaybeFooId = map constructMaybeFooId texts
                constructMaybeFooId :: Text -> Maybe FooId
                constructMaybeFooId x = case TR.decimal x of 
                        Left _ -> Nothing 
                        Right (el,_) -> Just $ Key (toPersistValue (el :: Int))

再次,非常感谢 David McBride 也为此!

于 2012-02-03T17:28:07.437 回答
1

希望我能提供帮助,但据我所知,yesod 与 Web 应用程序有关,因此我从未真正看过它。所以我可以试着在空中刺一下,也许我撞到了什么。

Hayoo导致

class MultiPiece s where
    fromMultiPiece :: [Text] -> Maybe s
    toMultiPiece :: s -> [Text]

Yesod.Dispatch. 由于FooId似乎有一个Read实例并且可能有一个Show实例,您可以尝试

{-# LANGUAGE TypeSynonymInstances #-}
-- maybe also FlexibleInstances

instance MultiPiece FooId where
    toMultiPiece foo = [Text.pack $ show foo]
    fromMultiPiece texts =
        case reads (unpack $ Text.concat texts) :: [(FooId,String)] of
          [(foo,_)] -> Just foo
          _         -> Nothing

我不知道这是否接近正确的东西,我会把它作为评论发布,但它太长了,评论中没有太多的格式。如果它没有帮助,我将删除它,以免给人的印象是您的问题已经有了答案,而实际上还没有答案。

于 2012-02-02T17:35:56.213 回答
0

我对 Yesod 也很陌生,我放弃了并将 -XTypeSynonymInstances 添加到我的 .cabal 文件中的 ghc-options 中,到目前为止,它让我的生活变得更加轻松。我不确定这是否是对这个特定问题的最优雅的答案,但否则我预测你会经常遇到这个别名实例错误。PS 尝试 id = (Key (PersistInt 64 n))

于 2012-02-02T20:13:33.090 回答