我有以下功能:
sendq :: Socket -> B.ByteString -> String -> IO PortNumber -> IO ()
sendq s datastring host port = do
hostAddr <- inet_addr host
sendAllTo s datastring (SockAddrInet port hostAddr)
Run Code Online (Sandbox Code Playgroud)
而sendAllTo具有功能签名
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
Run Code Online (Sandbox Code Playgroud)
问题是我以前的函数传递了一个IO PortNumber,其中SockAddr只接受一个PortNumber.我试图通过将sendAllTo提升到IO monad来使这两者兼容:
liftM sendAllTo s datastring (SockAddrInet port hostAddr)
Run Code Online (Sandbox Code Playgroud)
但没有快乐.告诉我许多论点.这是liftM的案例吗?我该如何正确应用?
liftM和朋友们一样,为了将纯粹的功能提升到一个单一的环境,就像Applicative组合者一样.
liftM :: Monad m => (s -> t) -> m s -> m t
Run Code Online (Sandbox Code Playgroud)
您尝试的代码有两个问题.一个是缺少括号.
liftM sendAllTo :: IO Socket -> IO (ByteString -> SockAddr -> IO ())
Run Code Online (Sandbox Code Playgroud)
这不是你的意思.另一个问题是
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
Run Code Online (Sandbox Code Playgroud)
是一个monadic操作,因此提升它将提供两层IO.通常的方法是将应用程序的纯前缀括起来,就像这样
liftM (sendAllTo s datastring) :: IO SockAddr -> IO (IO ())
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用构建参数liftM2.
liftM2 SockAddrInet ioport (inet_adder host) :: IO SockAddr
Run Code Online (Sandbox Code Playgroud)
那给了你
liftM (sendAllTo s datastring) (liftM2 SockAddrInet ioport (inet_adder host))
:: IO (IO ())
Run Code Online (Sandbox Code Playgroud)
因为它解释了如何计算操作但实际上并没有调用它,因此它将完全无法实现.这就是你需要的地方
join (liftM (sendAllTo s datastring) (liftM2 SockAddrInet ioport (inet_addr host)))
:: IO ()
Run Code Online (Sandbox Code Playgroud)
或者,更紧凑
sendAllTo s datastring =<< liftM2 SockAddrInet ioport (inet_adder host)
Run Code Online (Sandbox Code Playgroud)
插头.该斯特拉斯克莱德哈斯克尔增强支持成语支架,其中
(|f a1 .. an|) :: m t如果f :: s1 -> ... -> sn -> t和a1 :: m s1...... an :: m sn.
这些做同样的工作,Applicative m为liftM家族做的单子,治疗f作为一个纯粹的n元函数a1.. an作为effectful参数.Monads可以而且应该Applicative也是如此
(|SockAddrInet ioprot (inet_addr host)|) :: IO SockAddr
Run Code Online (Sandbox Code Playgroud)
和
(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|)|) :: IO (IO ())
Run Code Online (Sandbox Code Playgroud)
然后,该表示法允许您使用后缀调用上述计算的monadic计算@.
(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
Run Code Online (Sandbox Code Playgroud)
请注意,我仍然将应用程序的纯前缀括起来,以便f模板的整数(sendAllTo s datastring).符号允许您使用a在任何位置标记纯参数~,因此您可以编写此参数
(|sendAllTo ~s ~datastring (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
Run Code Online (Sandbox Code Playgroud)
如果心情带你.
咆哮.我们花了太多的精力找出正确的liftM,join,=<<,do,(|...~...@|)标点符号,以解释如何削减一个类型的值,解释内核(()这里)在解释影响上下文(IO在这里).如果在类型中更明确地进行这种向上切割,我们应该在程序中需要更少的噪声来将值和计算分成对齐.我应该更喜欢计算机来推断~和@标记的去向,但事实上,Haskell类型的文档太模糊,无法实现.