33

我最近开始阅读 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 的工作方式很有趣,并且喜欢用它来做我的小家务。

我从未听说过单独的 pi-calculus 或 CSP 通道。抱歉,如果这个问题看起来不恰当,这可能是我对此事的巨大无知的错。

你是对的,我应该更具体地说明我想在 Haskell 中复制的 Go 模式,我会尝试将问题编辑得更具体。但不要指望深刻的理论问题。问题是,从我阅读和编码的一些东西来看,Go 似乎有一种巧妙的方式来实现并发(在我的情况下,这只是意味着我的工作让我的所有核心都在数值计算中嗡嗡作响),并且如果我可以在 Haskell 中使用类似的语法,我会很高兴。

4

2 回答 2

37

我认为您正在寻找的是 来自 Base的Control.Concurrent.Chan 。除了明显的haskellifications之外,我还没有发现它与go的chans有任何不同。频道并不是什么特别的东西,请查看有关它的 wiki 页面

通道是称为通信顺序进程 (CSP)的更一般概念的一部分,如果您想在 Haskell 中以 CSP 的风格进行编程,您可能需要查看Communicating Haskell Processes (CHP)包。

CHP 只是在 Haskell 中进行并发的一种方式,请查看Haskellwiki 并发页面以获取更多信息。我认为您的用例可能最好使用Data Parrallel Haskell编写,但是这目前正在进行中,因此您现在可能想使用其他东西。

于 2010-12-23T20:56:08.397 回答
2

扩展 HaskellElephant 的答案,Control.Concurrent.Chan 是通道的方式,而 Control.ConcurrentforkIO可以模拟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

(警告:未经测试的代码)

于 2016-07-09T00:54:43.693 回答