我试图从Haskell中的输入读取行,直到找到非空行。 实际上,我只知道如何使用以下代码:
notEmpty [] = return ""
notEmpty (l:xs) = do
s <- l
if s /= "" then return s
else notEmpty xs
getLine' = notEmpty $ repeat getLine
测试(我打了两个空行然后&#39; foo&#39;):
*> getLine'
foo
"foo"
然而,为了锻炼,我试图使用Monoids(http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids)来尝试实现这一点,试图模仿First / getFirst Monoid(参见链接)。
我首先在符合我需要的列表上创建了一个Monoid(连接只保留第一个参数):
newtype FirstSt a = FirstSt { getFirstSt :: [a] }
deriving (Eq, Ord, Read, Show)
instance Monoid (FirstSt a) where
mempty = FirstSt []
FirstSt [] `mappend` x = x
FirstSt s `mappend` _ = FirstSt s
在无限的字符串列表中运行良好(感谢懒惰):
> getFirstSt . mconcat . map FirstSt $ ["", "", "foo", "", "bar"] ++ repeat ""
"foo"
但是,我无法让它在IO Monad中工作。我尝试了以下方法:
ioFirstSt = (=<<) (return . FirstSt)
getLine'' = getFirstSt <$> mconcat <$> (sequence . map ioFirstSt $ repeat getLine)
哪种类型正确:
*> :t getLine''
getLine'' :: IO [Char]
然而,Haskell一直希望在将其提供给mconcat
之前评估整个列表...
在Monoid / Monad范围内导航时有一种保持懒惰的方法吗?
答案 0 :(得分:1)
你的想法非常好。 Monoid是一个很好的结构,但遗憾的是,正如bheklilr指出的那样,sequence
将会执行所有IO。
制作instance Monoid (IO String)
会很好,但我们必须将它包装在newtype
中才能编译,但是我们会失去与其他IO的互操作性,所以让我们来吧在没有实例的情况下编写函数。
我喜欢使用<>
而不是mappend
,但它已被采用,<|>
也用于Alternative
,这就像应用函子的Monoid结构一样,你当然应该调查它。我在this answer中写了一些关于Alternative的文章。
无论如何,让我们使用<||>
并复制<>
的固定性:
infixr 6 <||>
我们可以从IO String
中生成一个monoid,因为我们可以检查返回的值以查看它是否为""
,然后执行下一个操作。这相当于使用==
检查我们是否有mempty
,因此只要IO s
是带有Eq实例的Monoid,我们就可以推广到s
。其次,我们不需要它IO
,我们可以使用任何Monad:
(<||>) :: (Monoid s, Eq s, Monad m) => m s -> m s -> m s
m <||> n = do
x <- m
if x == mempty then n else return x
请注意,这对于计算n
很懒惰 - 如果我们对m
的输出感到满意,则不会感到烦恼。然后,我们可以定义main = getLine <||> getLine <||> getLine >>= print
,为用户提供最多3次非空白的机会供我们打印。
数学上这是一个带有身份的幺半群
msempty :: (Monoid s, Monad m) => m s
msempty = return mempty
我们还要定义mconcat :: Monoid s => [s] -> s
:
msconcat :: (Monoid s, Eq s, Monad m) => [m s] -> m s
msconcat = foldr (<||>) (return mempty)
这让我们可以重写为main = msconcat [getLine,getLine,getLine] >>= print
这里对懒惰的真正考验是无限的行动清单:
main = msconcat (repeat getLine) >>= print
工作正常,并且如果用户做了除了什么都不输入之外的其他事情,则在有限的时间内终止。万岁!