这是使用liftM的情况吗?

J F*_*sch 3 haskell

我有以下功能:

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的案例吗?我该如何正确应用?

pig*_*ker 9

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 -> ta1 :: m s1...... an :: m sn.

这些做同样的工作,Applicative mliftM家族做的单子,治疗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类型的文档太模糊,无法实现.

  • 是.基本上,你写下你想要的应用程序结构,忽略monad; 那么你看看什么是纯粹的,什么是有效和斑点适当的提升标点符号. (3认同)