在Haskell中使用Maybe
似乎非常困难。在许多令人沮丧的编译错误之后,我能够实现我需要的功能,但它仍然完全混乱,我不知道如何改进它。
我需要:
... Maybe
提取为一个,最终Maybe ...
a -> b -> IO ()
与Just a
或Just b
或*(*)IO
以下是移除a -> b -> IO ()
部分的示例。我需要(a,b) -> IO ()
,而不是mapM_
,但我无法弄清楚如何传递两个参数(我只能import Network.URI
type URL = String
type Prefix = String
fubar :: String -> Maybe (Prefix, URL)
fubar url = case parseURI url of
Just u -> (flip (,) $ url)
<$> (fmap ((uriScheme u ++) "//" ++ ) ((uriRegName <$> uriAuthority u)))
_ -> Nothing
只有一个参数)。
> fubar "https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Data.Foldable.html#mapM"
Just ("https://hackage.haskell.org"
,"https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Data.Foldable.html#mapM"
)
结果:
{{1}}
(*)打印无法解析错误的内容
答案 0 :(得分:11)
使用do
表示法写的非常简单:
fubar :: String -> Maybe (Prefix, URL)
fubar url = do
u <- parseURI url
scheme <- uriScheme u
domain <- uriRegName <$> uriAuthority u
return $ (scheme ++ "//" ++ domain, url)
一般的Monads(特别是可能)都是将m (m a)
合并到m a
。每个<-
绑定都是对>>=
的调用的替代语法,如果它看到Nothing,则负责中止的操作员,以及以其他方式打开Just for you。
答案 1 :(得分:5)
首先请注意,您只需使用fmap
堆叠多个α <$> (fmap β (γ <$> uriAuthority u))
。这可以(仿函数法则!)重写α . β . γ <$> uriAuthority u
,即
{-# LANGUAGE TupleSections #-}
...
Just u -> (,url) . ((uriScheme u++"//") ++ ) . uriRegName <$> uriAuthority u
实际上保持图层分离的可读性可能更好,但是你也应该按照合金建议给它们命名。
此外,更强烈:
将多个嵌套
M
提取为一个,最终M
嗯,听起来像是monad,不是吗?
fubar url = do
u <- parseURI url
(,url) . ((uriScheme u++"//") ++ ) . uriRegName <$> uriAuthority u
答案 2 :(得分:3)
我并不完全清楚你所问的是什么,但我会尽力回答你提出的问题。
要将多个嵌套Maybe
提取到单个最终Maybe
中,需要Maybe
的monad-nature(也是应用性质)来处理。具体如何做取决于它们是如何嵌套的。
最简单的例子:
Control.Monad.join :: (Monad m) => m (m a) -> m a
-- thus
Control.Monad.join :: Maybe (Maybe a) -> Maybe a
元组:
squishTuple :: (Maybe a, Maybe b) -> Maybe (a,b)
squishTuple (ma, mb) = do -- do in Maybe monad
a <- ma
b <- mb
return (a,b)
-- or
squishTuple (ma, mb) = liftA2 (,) ma mb
清单:
sequenceA :: (Applicative f, Traversable t) => t (f a) -> f (t a)
-- thus
sequenceA :: [Maybe a] -> Maybe [a]
-- (where t = [], f = Maybe)
通过组合这些结构并遵循类型,可以平整其他结构。例如:
flattenComplexThing :: (Maybe a, [Maybe (Maybe b)]) -> Maybe (a, [b])
flattenComplexThing (ma, mbs) = do
a <- ma
bs <- (join . fmap sequenceA . sequenceA) mbs
return (a, bs)
join . fmap sequenceA . sequenceA
行有点复杂,需要一些时间来了解如何构建这样的东西。我的大脑以一种非常类型导向的方式工作(从右到左阅读构图):
[Maybe (Maybe b)]
|
sequenceA :: [Maybe _] -> Maybe [_]
↓
Maybe [Maybe b]
|
-- sequenceA :: [Maybe b] -> Maybe [b]
-- fmap f makes the function f work "inside" the Maybe, so
fmap sequenceA :: Maybe [Maybe b] -> Maybe (Maybe [b])
↓
Maybe (Maybe [b])
|
join :: Maybe (Maybe _) -> Maybe _
↓
Maybe [b]
关于第二个问题,如果你有a -> b -> IO ()
和Maybe a
,如何做Maybe b
,假设你根本不想在这个案件中采取行动任何一个都是Nothing
,你只做一些体操:
conditional :: (a -> IO ()) -> Maybe a -> IO ()
conditional = maybe (return ())
conditional2 :: (a -> b -> IO ()) -> Maybe a -> Maybe b -> IO ()
conditional2 f ma mb = conditional (uncurry f) (liftA2 (,) ma mb)
我再一次在脑海中找到了conditional2
主要以类型为导向的方式。
开发类型体操需要一些时间,但随后开始变得非常有趣。为了使这样的代码可读,使用辅助函数是很重要的,例如,上面conditional
,并将它们命名为好(这可以说是conditional
:-)。您将逐渐熟悉标准库的帮助程序。这里没有灵丹妙药,你只需要习惯它 - 它是一种语言。使用它,努力清晰,如果有些太难看,请尽量使它更漂亮。并提出更具体的问题: - )