liftM
和朋友的目的是将纯函数提升到一元设置,就像Applicative
组合器所做的那样。
liftM :: Monad m => (s -> t) -> m s -> m t
您尝试的代码有两个问题。一是缺少括号。
liftM sendAllTo :: IO Socket -> IO (ByteString -> SockAddr -> IO ())
这不是你的意思。另一个问题是
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
是一个单子操作,所以提升它会提供两层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 增强支持成语方括号,其中
(|f a1 .. an|) :: m t
如果f :: s1 -> ... -> sn -> t
和a1 :: m s1
... an :: m sn
_
它们的作用与家庭对 monad 的作用相同Applicative m
,liftM
将f
其视为纯 n 元函数,并将a1
..an
视为有效参数。Monad
s 可以也应该是Applicative
,所以
(|SockAddrInet ioprot (inet_addr host)|) :: IO SockAddr
和
(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|)|) :: IO (IO ())
然后,该表示法允许您调用像上面这样的计算单子计算,后缀为@
.
(|(sendAllTo s datastring) (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
请注意,我仍然将应用程序的纯前缀括起来,因此f
模板的(sendAllTo s datastring)
. 该符号允许您在任何位置用 a 标记纯参数~
,因此您可以这样写
(|sendAllTo ~s ~datastring (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()
如果心情带你。
咆哮。我们花费太多精力来找出正确的liftM
, join
, =<<
, do
,(|...~...@|)
标点符号,以便解释如何()
在效果解释上下文(此处)中将类型切割为值解释内核(IO
此处)。如果这种向上切割在类型中更明确,我们应该在程序中需要更少的噪音来将值和计算对齐。我更喜欢计算机来推断~
和@
标记的去向,但就目前情况而言,Haskell 类型的文档太模糊,无法实现。