3

私は次の機能を持っています:

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 ()

問題は、SockAddrがPortNumberのみを取得するIOPortNumberを取得する以前の関数からです。sendAllToをIOモナドにプロモートすることで、これら2つを互換性のあるものにしようとしました。

liftM sendAllTo s datastring (SockAddrInet port hostAddr)

しかし、喜びはありません。多くの議論について何かを教えてくれます。これはliftMの場合ですか?どうすれば正しく適用できますか?

4

3 に答える 3

9

liftMそして友人は、コンビネーターが行うのと同じように、純粋関数を単調な設定にジャッキアップするという目的を果たしApplicativeます。

liftM :: Monad m => (s -> t) -> m s -> m t

試したコードには2つの問題があります。1つは、かっこがないことです。

liftM sendAllTo :: IO Socket -> IO (ByteString -> SockAddr -> IO ())

それはあなたが意図したことではありません。もう一つの問題は

sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()

単調な操作であるため、持ち上げると2層のが配信され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は、イディオムブラケットをサポートしています。

(|f a1 .. an|) :: m tiff :: s1 -> ... -> sn -> ta1 :: m s1...。an :: m sn_

Applicative mこれらは、liftM家族がモナドに対して行うのと同じ仕事をしf、純粋なn-ary関数として扱い、効果的な引数として扱いますa1。sもそうすることができ、そうあるべきです。anMonadApplicative

(|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)。表記により、任意の位置の純粋な引数を。でマーク~できるため、これを記述できます。

(|sendAllTo ~s ~datastring (|SockAddrInet ioprot (inet_addr host)|) @|) :: IO ()

気分があなたを連れて行くなら。

暴言。効果を説明するコンテキスト(ここ)で値を説明するカーネル(ここ)として型を切り取る方法を説明するために、正しい、、、、句読点を理解liftMすることに非常に多くのエネルギーを費やしています。このアップカットが型でより明示的に行われた場合、値と計算を調整するためにプログラムで必要なノイズが少なくなるはずです。私はコンピューターがandマークがどこに行くのかを推測することをはるかに好むべきですが、現状では、Haskellタイプは文書が曖昧すぎてそれを実現できません。join=<<do(|...~...@|)()IO~@

于 2012-08-19T11:45:32.723 に答える
4

<-このように使用して、IOモナドからポート番号を抽出できるはずです。

sendq s datastring host ioport = do
     hostAddr <- inet_addr host
     port     <- ioport
     sendAllTo s datastring (SockAddrInet port hostAddr)
于 2012-08-19T10:22:17.380 に答える
3

どうですか:

sendq s datastring host ioport = (SockAddrInet <$> inet_addr host <*> ioport)
     >>= sendAllTo s datastring

を作成してから標準を使用しますliftM(実際には、私は好み<$>ますが、同じものです)。SockAddrInet>>=

重要なのは、式の簡単に持ち上げることができる部分だけを持ち上げてから、モナドを処理する他の方法を使用して残りのコードを形成することです。これにより、簡潔で読みやすいコードが得られます。

于 2012-08-19T11:46:46.090 に答える