Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I emulate Go's channels with Haskell?

I recently started reading about the Go programming language and I found the channel variables a very appealing concept. Is it possible to emulate the same concept in Haskell? Maybe to have a data type Channel a and a monad structure to enable mutable state and functions that work like the keyword go.

I'm not very good in concurrent programming and a simple channel passing mechanism like this in Haskell would really make my life easier.

EDIT

People asked me to clarify what kind of Go's patterns I was interested in translating to Haskell. So Go has channel variables that are first class and can be passed around and returned by functions. I can read and write to these channels, and so communicate easily between routines that can run concurrently. Go also has a go keyword, that according to the language spec initiates the execution of a function concurrently as an independent thread and continues to execute the code without waiting.

The exact pattern I'm interested in is something like this (Go's syntax is weird - variables are declared by varName varType instead of the usual inverted way - but I think it is readable):

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

My primary interest is to do Monte Carlo simulations, in which I generate configurations sequentially by trying to modify the current state of the system and accepting the modification if it satisfies some criteria.

The fact the using those channel stuff I could write a very simple, readable and small Monte Carlo simulation that would run in parallel in my multicore processor really impressed me.

The problem is that Go have some limitations (specially, it lacks polymorphism in the way I'm accustomed to in Haskell), and besides that, I really like Haskell and don't wanna trade it away. So the question is if there's some way to use some mechanics that looks like the code above to do a concurrent simulation in Haskell easily.

EDIT(2, context): I'm not learned in Computer Science, specially in concurrency. I'm just a guy who creates simple programs to solve simple problems in my daily research routine in a discipline not at all related to CS. I just find the way Haskell works interesting and like to use it to do my little chores.

I never heard about alone pi-calculus or CSP channels. Sorry if the question seems ill posed, it's probably my huge-ignorance-about-the-matter's fault.

You are right, I should be more specific about what pattern in Go I'd like to replicate in Haskell, and I'll try to edit the question to be more specific. But don't expect profound theoretical questions. The thing is just that, from the few stuff I read and coded, it seems Go have a neat way to do concurrency (and in my case this just means that my job of making all my cores humming with numerical calculations is easier), and if I could use a similar syntax in Haskell I'd be glad.

like image 397
Rafael S. Calsaverini Avatar asked Dec 23 '10 20:12

Rafael S. Calsaverini


People also ask

How do I create a go channel?

Creating a Channel In Go language, a channel is created using chan keyword and it can only transfer data of the same type, different types of data are not allowed to transport from the same channel. You can also create a channel using make() function using a shorthand declaration.

Can you do something in Goroutines without channels?

Channels not only work for interactions between a goroutine and the main programs, they also provide a way to communicate between different goroutine. For example, let's create a function that subtracts 3 to every result returned by timesThree but only if it's an even number.

How does Golang channel work?

In Golang, or Go, channels are a means through which different goroutines communicate. Think of them as pipes through which you can connect with different concurrent goroutines. The communication is bidirectional by default, meaning that you can send and receive values from the same channel.


2 Answers

I think what you are looking for is Control.Concurrent.Chan from Base. I haven't found it to be any different from go's chans other then the obvious haskellifications. Channels aren't something that is special to go, have a look at the wiki page about it.

Channels are part of a more general concept called communicating sequential processes (CSP), and if you want to do programming in the style of CSP in Haskell you might want to take a look at the Communicating Haskell Processes (CHP) package.

CHP is only one way of doing concurrency in Haskell, take a look at the Haskellwiki concurrency page for more information. I think your use case might be best written using Data Parrallel Haskell, however that is currently a work in progress, so you might want to use something else for now.

like image 149
HaskellElephant Avatar answered Oct 06 '22 10:10

HaskellElephant


Extending HaskellElephant's answer, Control.Concurrent.Chan is the way to go for channels and Control.Concurrent's forkIO can emulate the go keyword. To make the syntax a bit more similar to Go, this set of aliases can be used:

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

This can be used like so:

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 

(Warning: untested code)

like image 31
Tuomas Laakkonen Avatar answered Oct 06 '22 10:10

Tuomas Laakkonen