Haskell を使用して Go のチャネルをエミュレートするにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/4522387

  •  12-10-2019
  •  | 
  •  

質問

最近 Go プログラミング言語について読み始めたところ、チャネル変数が非常に魅力的な概念であることがわかりました。Haskell で同じ概念をエミュレートすることは可能ですか?おそらくデータ型があるはずです Channel a キーワードのように機能する可変状態と関数を可能にするモナド構造 go.

私は同時プログラミングがあまり得意ではないので、Haskell のこのようなシンプルなチャネル受け渡しメカニズムがあれば、作業が本当に楽になります。

編集

人々は私に、どのような Go のパターンを Haskell に変換することに興味があるのか​​明確にするよう求めました。したがって、Go にはファーストクラスのチャネル変数があり、関数によって受け渡したり返したりすることができます。これらのチャネルの読み取りと書き込みができるため、同時に実行できるルーチン間で簡単に通信できます。Goには、 go キーワードは、言語仕様に従って、独立したスレッドとして関数の実行を同時に開始し、待機せずにコードの実行を継続します。

私が興味がある正確なパターンは次のようなものです (Go の構文は奇妙です - 変数は通常の逆の方法ではなく varName varType によって宣言されます - しかし、読みやすいと思います):

func generateStep(ch chan int) {
      //ch is a variable of type chan int, which is a channel that comunicate integers
      for {
          ch <- randomInteger() //just sends random integers in the channel 
      }

func filter(input, output chan int) {
      state int
      for {
          step <- input  //reads an int from the input channel
          newstate := update(state, step) //update the variable with some update function
          if criteria(newstate, state) {
             state = newstate // if the newstate pass some criteria, accept the update
          } 
          output <- state    //pass it to the output channel
      } 
}

func main() {
    intChan := make(chan int) 
    mcChan  := make(chan int) 
    go generateStep(intChan)     // execute the channels concurrently
    go filter(intChan, mcChan)
    for i:=0; i<numSteps; i++  {
        x <- mcChan        // get values from the filtered channel
        accumulateStats(x)  // calculate some statistics
    } 
    printStatisticsAbout(x)
}

私の主な関心は、モンテカルロ シミュレーションを実行することです。システムの現在の状態を変更しようとし、それがいくつかの基準を満たしている場合にその変更を受け入れることによって、構成を順番に生成します。

これらのチャネルを使用して、マルチコア プロセッサで並列実行される、非常にシンプルで読みやすい小さなモンテカルロ シミュレーションを作成できたという事実に、私は本当に感銘を受けました。

問題は、Go にはいくつかの制限があることです (特に、私が慣れ親しんでいる Haskell の方法ではポリモーフィズムが欠如しています)。それに加えて、私は Haskell がとても気に入っており、それを手放したくありません。したがって、問題は、上記のコードのようなメカニズムを使用して、Haskell で同時シミュレーションを簡単に実行する方法があるかどうかです。

編集(2, コンテキスト):私はコンピューター サイエンス、特に同時実行については学んでいません。私は、CS とはまったく関係のない分野での日々の研究で、単純な問題を解決するための単純なプログラムを作成する単なる男です。私は Haskell の仕組みが面白くて、ちょっとした雑用をするのに使ってみたいと思っています。

単独の円周率計算や CSP チャネルについては聞いたことがありません。質問の仕方が悪いように思えたら申し訳ありませんが、それはおそらくこの件について非常に無知な私のせいです。

おっしゃる通りです。Go のどのパターンを Haskell で再現したいのかをもっと具体的にする必要があります。より具体的に質問を編集してみます。ただし、深い理論的な質問を期待しないでください。問題は、私が読んでコーディングしたいくつかの内容から、Go には同時実行を行うための巧妙な方法があるようだということです (そして私の場合、これは単に数値計算ですべてのコアを動作させる作業が簡単になることを意味します)。 Haskell で同様の構文を使用できれば幸いです。

役に立ちましたか?

解決

あなたが探しているものは次のとおりだと思います 制御.同時.ちゃん ベースから。明らかなハスケリフィケーション以外には、go's chans と何の違いもありません。チャンネルは特別なものではありません。 それに関するwikiページ.

チャネルは、と呼ばれるより一般的な概念の一部です。 通信シーケンシャルプロセス (CSP), Haskell の CSP スタイルでプログラミングを行いたい場合は、以下を参照するとよいでしょう。 Haskell プロセスの通信 (CHP) パッケージ。

CHP は Haskell で並行性を実現する 1 つの方法にすぎません。 Haskellwiki 同時実行ページ 詳細については。あなたのユースケースは、次のように記述するのが最適だと思います データ並列 Haskell, ただし、これは現在開発中であるため、現時点では別のものを使用することをお勧めします。

他のヒント

haskellelephantの答えを拡張するcontrol.concurrent.chanは、チャンネルとcontrol.concurrent'sに行く方法です。 forkIO エミュレートできます go キーワード。構文をGOよりももう少し似たものにするために、このエイリアスのセットを使用できます。

import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Concurrent.MVar (newMVar, swapMVar, readMVar)

data GoChan a = GoChan { chan :: Chan a, closed :: MVar Bool }

go :: IO () -> IO ThreadId
go = forkIO

make :: IO (GoChan a)
make = do
    ch <- newChan
    cl <- newMVar False
    return $ GoChan ch cl

get :: GoChan a -> IO a
get ch = do
    cl <- readMVar $ closed ch
    if cl
        then error "Can't read from closed channel!"
        else readChan $ chan ch

(=->) :: a -> GoChan a -> IO ()
v =-> ch = do
    cl <- readMVar $ closed ch
    if cl
        then error "Can't write to closed channel!"
        else writeChan (chan ch) v

forRange :: GoChan a -> (a -> IO b) -> IO [b]
forRange ch func = fmap reverse $ range_ ch func []
    where range_ ch func acc = do
        cl <- readMVar $ closed ch
        if cl
            then return ()
            else do
                v <- get ch
                func v
                range_ ch func $ v : acc
close :: GoChan a -> IO ()
close ch = do
    swapMVar (closed ch) True
    return ()

これは次のように使用できます。

import Control.Monad

generate :: GoChan Int -> IO ()
generate c = do
    forM [1..100] (=-> c)
    close c

process :: GoChan Int -> IO ()
process c = forRange c print

main :: IO ()
main = do
    c <- make
    go $ generate c
    process c

(警告:テストされていないコード)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top