我正在使用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)
这里有几件事:
- CCD_ 1具有签名CCD_。我意识到我必须做点什么才能把
peptideSequence
从Maybe ByteString
变成ByteString
,所以这似乎是必要的(而且做起来并不痛苦),但是 - 从
Maybe ByteString
转换为Float
似乎有点可笑。有更好的方法来处理这个问题吗?还是这只是需要向下推到calculationResults
函数中,并让它处理转换
我想我正试图从"在泡沫中学习haskell"扩展到包括它实际是如何完成的,而不是对编译器进行猛烈抨击,直到它最终放弃并说"好吧,我让它通过"。
提前感谢您的意见!
一些事情。
fromJust
相当邪恶。它相当于纯代码世界中的calculationResults
0。您正在从Maybe
monad中提取一个值,该值没有模式匹配。
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 monod中操作,那你就太倒霉了。Snap已覆盖fail的默认实现。
来自RWH:
小心失败许多Monad实例不会覆盖默认值我们在这里展示的fail的实现,所以在那些monad中,fail使用错误。调用错误通常是非常不可取的,因为引发调用方无法捕获或不会捕获的异常预料
即使你现在知道你在一个已经失败做一些更明智的事情,我们仍然建议避免太容易在以后重构时给自己带来问题代码,并忘记以前安全使用fail可能是危险的在新的背景下。
尽管如此,我仍然使用它,因为它在这种情况下是有意义的(除非Snap的作者想纠正我)
我会让CalculationResults
的结果返回JSON
的结果。我也会在CalculationResults
函数中处理Float
类型的转换,可能会使其更干净
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"是位于:: Float -> ByteString
1中的文件"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