Haskell:了解bind和>>函数

时间:2019-07-26 10:17:18

标签: haskell functional-programming monads io-monad

我有以下Haskell表达式:

a = getLine >>= putStrLn . filter isDigit >> a

我无法理解上述表达式的工作原理。我知道>>=函数采用一个monadic值和一个函数(采用一个正常值并返回monadic值),并返回一个monadic值。

我知道getLineputStrLn具有以下类型声明:

getLine :: IO String 
putStrLn :: String -> IO ()

因此,表达式的以下部分:

a = getLine >>= putStrLn . filter isDigit

将返回IO ()。但是,函数>>会获取一个第一单子值和一个第二单子值,并返回第二个单子值。

鉴于原始表达式,传递给>>的第一个参数的类型为IO String。第二个参数是a

我的问题是,a的类型是什么?上述表达式如何工作以连续获取用户输入并仅将输入的数字部分打印回屏幕?任何见解都会受到赞赏。

2 个答案:

答案 0 :(得分:6)

注意:我按照@SamuelBarr的建议将a函数重命名为readPrintLoop,因为这样可以避免一些混乱。

  

我的问题是readPrintLoop的类型是什么,上面的表达式如何工作以连续获取用户输入并将仅输入内容的数字部分打印回屏幕?

readPrintLoop具有类型:readPrintLoop :: IO a,因此它是IOa可以是任何类型,因为我们永远不会“返回”该值,所以我们永远不会结束此函数。

该功能不断重复,因为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