我有以下Haskell表达式:
a = getLine >>= putStrLn . filter isDigit >> a
我无法理解上述表达式的工作原理。我知道>>=
函数采用一个monadic值和一个函数(采用一个正常值并返回monadic值),并返回一个monadic值。
我知道getLine
和putStrLn
具有以下类型声明:
getLine :: IO String
putStrLn :: String -> IO ()
因此,表达式的以下部分:
a = getLine >>= putStrLn . filter isDigit
将返回IO ()
。但是,函数>>
会获取一个第一单子值和一个第二单子值,并返回第二个单子值。
鉴于原始表达式,传递给>>
的第一个参数的类型为IO String
。第二个参数是a
。
我的问题是,a
的类型是什么?上述表达式如何工作以连续获取用户输入并仅将输入的数字部分打印回屏幕?任何见解都会受到赞赏。
答案 0 :(得分:6)
注意:我按照@SamuelBarr的建议将a
函数重命名为readPrintLoop
,因为这样可以避免一些混乱。
我的问题是
readPrintLoop
的类型是什么,上面的表达式如何工作以连续获取用户输入并将仅输入内容的数字部分打印回屏幕?
readPrintLoop
具有类型:readPrintLoop :: IO a
,因此它是IO
。 a
可以是任何类型,因为我们永远不会“返回”该值,所以我们永远不会结束此函数。
该功能不断重复,因为readPrintLoop
是根据自身定义的。 readPrintLoop
定义为:
readPrintLoop :: IO a
readPrintLoop = getLine >>= putStrLn . filter isDigit >> readPrintLoop
我们这里具有无限递归,因为最终您将遇到a
,因此将其替换为另一个getLine >>= putStrLn . filter isDigit >> a
,依此类推。
但是,函数
>>
会获取一个第一个Monadic值和一个第二个Monadic值,并返回第二个Monadic值。
(>>)
等效于:
(>>) :: Monad m => m a -> m b -> m b
u >> v = m >>= (\_ -> v)
所以a
的实现等效于:
readPrintLoop :: IO a
readPrintLoop = getLine >>= putStrLn . filter isDigit >>= \_ -> readPrintLoop
这里下划线变量_
将传递给()
。
答案 1 :(得分:4)
a = getLine >>= putStrLn . filter isDigit
不是 “表达的一部分”。
getLine >>= putStrLn . filter isDigit
是表达式的一部分。而且它不是“ 返回 IO ()
”。它的类型为IO ()
(您已经正确推断为(*))。它是您所说的“一元数值” 。
为其命名,any name,
ioAction :: IO ()
ioAction = getLine >>= (putStrLn . filter isDigit)
我们最终以
a = ioAction >> a
----------------------------------
(>>) :: IO a -> IO b -> IO b
ioAction :: IO ()
a :: IO b
----------------------------------
a :: IO b
以及所有类型检查。
a
在
a = ((>>) ioAction) a
由>>
的语义定义。
(*)
----------------------------------------------------
(>>=) :: m a -> (a -> m b) -> m b
getLine :: IO String m a
putStrLn :: String -> IO ()
putStrLn . filter isDigit :: String -> IO () a -> m b
---------------------------------------------------- ------------
getLine >>= (putStrLn . filter isDigit) :: IO () m b