清理Snap Route Handler(Haskell)

时间:2013-11-20 02:42:05

标签: haskell haskell-snap-framework

我正在使用针对haskell的snap框架创建一个网站,而且我仍然是haskell(和snap)的新手。我希望找到一种“更好”的方式来编写这个路由处理程序。

possibleMatches :: Snap ()
possibleMatches = do
  peptideSequence <- getParam "peptide_sequence"
  foundWeight     <- getParam "weight"
  let results = calculationResults (read . C8.unpack $ (fromJust foundWeight)) (fromJust peptideSequence)
  maybe (writeBS "must specify params in URL")
         writeJSON $ Just (results)

这里有几件事情:

  1. calculationResults有签名:: Float -> ByteString。我意识到我必须做一些事情才能将peptideSequenceMaybe ByteString传递到ByteString,这样看起来很有必要(而且不是非常痛苦),但是
  2. Maybe ByteString转换为Float似乎有点荒谬。有没有更好的方法来处理这个?或者这只是需要被推入calculationResults函数并让它处理转换的东西?
  3. 我想我正试图从“学习泡沫中的haskell”扩展到包括它实际上是如何完成的,而不是在编译器上敲击它直到它最终放弃并说“好我会让它通过”

    提前感谢您的意见!

1 个答案:

答案 0 :(得分:6)

一些事情。

fromJust非常邪恶。它等于纯代码世界中的unsafePerformIO。您正在从Maybe monad w / o模式匹配中提取一个值。

fromJust :: Maybe a -> a
unsafePerformIO :: IO a -> a

> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing

现在,很可能不会恶意操纵您的HTML,以便这些参数返回Nothing。但是,如果确实发生了这种情况,你应该使用Snap的内置故障机制,它是Monad类中的fail的Snaps实现。它比fromJust更安全,并且由模式匹配失败触发。您可以通过getParam上的模式匹配来使用它。 (Just peptideSequence <- getParam "peptide_sequence"

instance Monad Snap where
    (>>=)  = snapBind
    return = snapReturn
    fail   = snapFail

snapFail实现为

snapFail :: String -> Snap a
snapFail !m = Snap $! return $! PassOnProcessing m

PassOnProcessing将优雅地处理模式匹配失败。

更多信息在代码中:

  

http://hackage.haskell.org/package/snap-core-0.8.0.1/docs/src/Snap-Internal-Types.html

旁注:

所有monad都有默认的fail实现,但如果没有覆盖,结果通常是不合需要的。任何不受欢迎的我都意味着它会抛出一个只能在IO monad中捕获的异常,但是如果你不是在IO monad中运行,那么你运气不好。 Snap已覆盖默认的失败实现。

来自RWH:

  

小心失败。   许多Monad实例不会覆盖默认值   我们在这里展示的失败的实现,所以在那些monad中,失败了   使用错误。调用错误通常是非常不受欢迎的,因为它   抛出一个异常,即调用者无法捕获或不会捕获   期望的。

     

即使你现在知道你正在执行一个monad   失败做一些更明智的事情,我们仍然建议避免它。它的   当你重构你的时候,太容易让自己成为一个问题   代码并忘记以前安全使用失败可能是危险的   在新的背景下。

我仍然使用它,因为它在这种情况下是有意义的(除非Snap作者想要纠正我)

我会得到CalculationResults返回JSON结果的结果。我还会在Float函数中处理CalculationResults类型转换,可能会使其更清晰

possibleMatches :: Snap ()
possibleMatches = do
  Just peptideSequence <- getParam "peptide_sequence"
  Just foundWeight     <- getParam "weight"
  writeJSON $ calculationResults (read $ C8.unpack foundWeight) peptideSequence

possibleMatches :: Handler App (AuthManager App) ()
possibleMatches = do
  (peptideSequence, foundWeight) <- (,) <$> getParam "peptide_sequence" <*> getParam "weight"
  writeJSON $ calculationResults (read $ C8.unpack foundWeight) peptideSequence

更新:

要在Snap中进行更强大的错误处理,您可以使用以下代码:

catchError :: HasHeist b => ByteString -> Handler b v () -> Handler b v ()
catchError msg action = action `catch` \(e::SomeException) -> go
    where go = do logError msg
                  modifyResponse $ setResponseCode 500
                  render "500"

其中“500”是位于"500.tpl"的文件snaplets/heist/templates/500.tpl的名称 要将其应用于您的某个处理程序,您可以执行以下操作:

handleNewUser :: Handler App (AuthManager App) ()
handleNewUser = method GET handleGet <|> method POST handlePost
  where
    handleGet = currentUser >>= maybe the404 (\_ -> render "profile")
    handlePost = catchError "Error during login" $ do
          setTimeout 100
          Just login <- getParam "login"
          if | isValid login -> do user <- registerUser "login" "password"
                                   either (\_ -> render "error") (handleUser login) user
             | otherwise -> render "error"
    handleUser = -- etc etc