我有以下功能:
sendq :: Socket -> B.ByteString -> String -> IO PortNumber -> IO ()
sendq s datastring host port = do
hostAddr <- inet_addr host
sendAllTo s datastring (SockAddrInet port hostAddr)
而sendAllTo具有函数签名
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
问题在于我以前的函数传递了一个IO PortNumber,其中SockAddr仅获取PortNumber。我试图通过将sendAllTo提升到IO monad中来使这两者兼容:
liftM sendAllTo s datastring (SockAddrInet port hostAddr)
但没有快乐。告诉我许多论点。这是liftM的案例吗?我该如何正确应用?
答案 0 :(得分:9)
liftM
和朋友的目的是将纯函数升级为monadic设置,就像Applicative
组合器一样。
liftM :: Monad m => (s -> t) -> m s -> m t
您尝试的代码存在两个问题。一个是缺少括号。
liftM sendAllTo :: IO Socket -> IO (ByteString -> SockAddr -> IO ())
这不是你的意思。另一个问题是
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
是 monadic 操作,因此提升它将提供两层IO
。通常的方法是将应用程序的纯前缀括起来,如此
liftM (sendAllTo s datastring) :: IO SockAddr -> IO (IO ())
然后,您可以使用liftM2
构建参数。
liftM2 SockAddrInet ioport (inet_adder host) :: IO SockAddr
那会给你
liftM (sendAllTo s datastring) (liftM2 SockAddrInet ioport (inet_adder host))
:: IO (IO ())
它将完全没有实现任何目标,因为它解释了如何计算操作但实际上并没有调用它!那就是你需要的地方
join (liftM (sendAllTo s datastring) (liftM2 SockAddrInet ioport (inet_addr host)))
:: IO ()
或更紧凑
sendAllTo s datastring =<< liftM2 SockAddrInet ioport (inet_adder host)
即插即 Strathclyde Haskell Enhancement支持idiom brackets,其中
(|f a1 .. an|) :: m t
如果f :: s1 -> ... -> sn -> t
和a1 :: m s1
... an :: m sn
。
这些Applicative m
与liftM
家庭为monad做同样的工作,将f
视为纯粹的n-ary函数和a1
.. {{1作为有效的参数。 an
可以而且应该Monad
,所以
Applicative
和
(|SockAddrInet ioprot (inet_addr host)|) :: IO SockAddr
然后,该表示法允许您使用后缀(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|)|) :: IO (IO ())
来调用上述计算的monadic计算。
@
请注意,我仍然使用应用程序的纯前缀括号,因此模板的(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
是整个f
。符号允许您使用(sendAllTo s datastring)
在任何位置标记纯参数,因此您可以编写此
~
如果心情带你。
Rant。我们花费了太多精力来确定正确的(|sendAllTo ~s ~datastring (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
,liftM
,join
,=<<
,{{1标点符号是为了解释如何在效果解释上下文(do
这里)中将类型作为值解释内核(此处为(|...~...@|)
)。如果在类型中更明确地进行这种向上切割,我们应该在程序中需要更少的噪声来将值和计算分成对齐。我更倾向于使用计算机来推断()
和IO
标记的位置,但事实上,Haskell类型的文档太模糊,无法实现。
答案 1 :(得分:4)
您应该可以使用<-
这样
sendq s datastring host ioport = do
hostAddr <- inet_addr host
port <- ioport
sendAllTo s datastring (SockAddrInet port hostAddr)
答案 2 :(得分:3)
怎么样:
sendq s datastring host ioport = (SockAddrInet <$> inet_addr host <*> ioport)
>>= sendAllTo s datastring
我们使用liftM
(实际上,我更喜欢<$>
,但它们是相同的)来构建SockAddrInet
,然后使用标准>>=
。
关键是只使用可以轻松解除的表达式部分,然后使用其他方式处理monad以形成其余的代码。这提供了简洁易读的代码。