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