Как я могу эмулировать каналы Go с Haskell? - PullRequest
29 голосов
/ 23 декабря 2010

Недавно я начал читать о языке программирования Go и нашел переменные канала очень привлекательной концепцией. Возможно ли подражать той же концепции в Haskell? Возможно иметь тип данных Channel a и структуру монады для включения изменяемого состояния и функций, которые работают как ключевое слово go.

Я не очень хорош в параллельном программировании, и такой простой механизм передачи каналов, как этот, в Haskell, действительно облегчит мою жизнь.

EDIT

Люди просили меня уточнить, какие паттерны Го я хотел бы перевести на Хаскелл. Таким образом, 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, я был бы рад.

Ответы [ 2 ]

32 голосов
/ 23 декабря 2010

Я думаю, что вы ищете Control.Concurrent.Chan из базы. Я не обнаружил, что он чем-то отличается от чан Го, кроме очевидных хакеллизаций. Каналы не являются чем-то особенным, посмотрите на вики-страницу об этом .

Каналы являются частью более общей концепции, называемой передачей последовательных процессов (CSP) , и если вы хотите заниматься программированием в стиле CSP на Haskell, вы можете взглянуть на Коммуникация процессов Haskell (CHP) пакет.

CHP - это всего лишь один из способов обеспечения параллелизма в Haskell, для получения дополнительной информации посетите страницу параллелизма Haskellwiki *1014*. Я думаю, что ваш сценарий использования может быть лучше всего написан с использованием Data Parrallel Haskell , однако это в настоящее время находится в стадии разработки, так что вы можете использовать что-то еще сейчас.

1 голос
/ 09 июля 2016

Расширяя ответ HaskellElephant, Control.Concurrent.Chan - это путь для каналов, а Control.Concurrent 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

(Предупреждение: непроверенный код)

...