Как я могу эмулировать каналы Go с Haskell?
Недавно я начал читать о языке программирования Go и нашел переменные канала очень привлекательной концепцией. Можно ли эмулировать ту же концепцию в Haskell? Может быть, иметь тип данных Channel a
и монадная структура для включения изменяемого состояния и функций, которые работают как ключевое слово 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, я был бы рад.
2 ответа
Я думаю, что вы ищете, это Control.Concurrent.Chan из базы. Я не обнаружил, что он чем-то отличается от чан Го, кроме очевидных хаскеллизаций. Каналы не являются чем-то особенным, посмотрите на страницу вики об этом.
Каналы являются частью более общей концепции, называемой передачей последовательных процессов (CSP), и если вы хотите заняться программированием в стиле CSP в Haskell, вы можете взглянуть на пакет Communicating Haskell Processes (CHP).
CHP - это только один из способов обеспечения параллелизма в Haskell. Для получения дополнительной информации посетите страницу параллелизма в Haskellwiki. Я думаю, что ваш вариант использования может быть лучше всего написан с использованием Data Parrallel Haskell, однако в настоящее время он находится в стадии разработки, так что вы можете использовать что-то еще сейчас.
Расширяя ответ на 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
(Предупреждение: непроверенный код)