在haskell中读取行,直到非空字符串

在haskell中读取行,直到非空字符串,haskell,io,getline,string,monoids,Haskell,Io,Getline,String,Monoids,我试图从Haskell的输入中读取行,直到找到一个非空行。 实际上,我知道如何简单地使用以下代码: notEmpty [] = return "" notEmpty (l:xs) = do s <- l if s /= "" then return s else notEmpty xs getLine' = notEmpty $ repeat getLine 然而,为了练习,我尝试使用Monoids()实现这一点,尝试模仿First/getFirst

我试图从Haskell的输入中读取行,直到找到一个非空行。 实际上,我知道如何简单地使用以下代码:

notEmpty [] = return ""
notEmpty (l:xs) = do
  s <- l
  if s /= "" then return s
             else notEmpty xs

getLine' = notEmpty $ repeat getLine
然而,为了练习,我尝试使用Monoids()实现这一点,尝试模仿First/getFirst Monoid(参见链接)

我首先在列表上创建了一个符合我需要的幺半群(串联只保留第一个参数):

这在无限的字符串列表上运行良好(由于懒惰):

然而,我无法让它在IO Monad中工作。我尝试了以下方法:

ioFirstSt = (=<<) (return . FirstSt)
getLine'' = getFirstSt <$> mconcat <$> (sequence . map ioFirstSt $ repeat getLine)
然而,Haskell一直希望在将整个列表提交给
mconcat
之前对其进行评估。。。
在Monoid/Monad范围内导航时,有没有办法保持懒惰?

您的思维非常棒。Monoid是一个很好的结构,但不幸的是,正如bheklillr指出的,
序列
将执行所有IO

理论与实践的较量,以及对替代方案的认同 制作
实例Monoid(IO字符串)
会很好,但我们必须将其包装在
newtype
中才能进行编译,但这样我们就失去了与其他IO的一些互操作性,所以让我们只编写没有实例的函数

我喜欢使用
而不是
mappend
,但是它被接受了,
也被接受为
替代
,这就像是应用程序函子的幺半群结构,您当然应该研究它。我在书中写了一点关于另类的东西

无论如何,让我们使用
并复制
的固定性:

请注意,这在计算
n
方面是懒惰的-如果我们对
m
的输出感到满意,这并不麻烦。然后,我们可以定义
main=getLine-getLine-getLine>>=print
,让用户最多有3次机会输入非空白的内容进行打印

标识和列表连接 从数学上讲,这是一个具有恒等式的幺半群

msempty :: (Monoid s, Monad m) => m s
msempty = return mempty
我们还要定义
mconcat::monoids=>[s]->s
的等价物:

msconcat :: (Monoid s, Eq s, Monad m) => [m s] -> m s
msconcat = foldr (<||>) (return mempty)

这很好,如果用户除了什么都不输入之外还做了其他事情,那么它会在有限的时间内终止。万岁

我相信
sequence
对于
IO
monad来说,它不会懒散到让它工作的程度,它会在返回结果之前尝试评估
IO
操作的整个列表。顺便说一句,如果您只需使用
getLine'=getLine>=\s->if s/=”,然后返回s else getLine'
,就可以避免中间无限列表。尽管如此,幺半群方法还是很有趣的。谢谢你非常详细的回答。对于IO单子来说,顺序似乎并不懒惰,这是有道理的,因为保证IO的顺序很重要。我在Monads/Monoid中还有很多东西要学!
ioFirstSt = (=<<) (return . FirstSt)
getLine'' = getFirstSt <$> mconcat <$> (sequence . map ioFirstSt $ repeat getLine)
*> :t getLine''
getLine'' :: IO [Char]
infixr 6 <||>
(<||>) :: (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
msempty :: (Monoid s, Monad m) => m s
msempty = return mempty
msconcat :: (Monoid s, Eq s, Monad m) => [m s] -> m s
msconcat = foldr (<||>) (return mempty)
main = msconcat (repeat getLine) >>= print