For me, Lisp and Haskell exercise two different conceptual muscles. And neither of them, nor C in general exercise the increasingly important concurrency muscle. Maybe Clojure is a good way to get both the Lisp and concurrency fixes, but something on the Erlang VM, or perhaps Go or Node, are more fundamentally designed around concurrency, and many people build those chops using threads in C++ or Java. Interestingly, this is another area where the usual suspects of Python, Ruby, and PHP are quite poor.
What about Go or Node make it more designed around concurrency than Haskell? It sounds like you are trying to say Erlang, Go, and Node have "first class concurrency". I'd agree that Erlang does, but I'd say that Node and Go do not. I'm almost tempted to wager the same is true for Lisp, but don't have enough experience with concurrency in Lisp.
Furthermore I'd wager that Go and Node have nothing in the realm of concurrency that Haskell doesn't have. If I'm wrong, I look forward to finding out ;)
"Languages aren't defined by what they make possible, but by what they make easy", to which I would add "and idiomatic". Go and Node make their (very different) concurrency features easy and idiomatic. Maybe Haskell does too, though, and I just don't know it well enough!
Here's 2 Examples (Warning: Didn't run them and could have missed imports).
One parallelizing a sudoku solver and the other making a reminder program concurrent. Both are from Parallel and Concurrent Programming In Haskell[0].
Sequential version[1]:
import Sudoku
import Control.Exception
import System.Environment
import Data.Maybe
main :: IO ()
main = do
[f] <- getArgs
file <- readFile f
let puzzles = lines file
solutions = map solve puzzles
print (length (filter isJust solutions))
Parallel version[2]:
import Sudoku
import Control.Exception
import System.Environment
import Data.Maybe
import Control.Parallel.Strategies (parMap) -- line that changed
main :: IO ()
main = do
[f] <- getArgs
file <- readFile f
let puzzles = lines file
solutions = runEval (parMap solve puzzles) -- line that changed
print (length (filter isJust solutions))
Here's an example making a small reminder program concurrent:
Sequential[3]:
import Control.Concurrent
import Text.Printf
import Control.Monad
main =
forever $ do
s <- getLine
forkIO $ setReminder s
setReminder :: String -> IO ()
setReminder s = do
let t = read s :: Int
printf "Ok, I'll remind you in %d seconds\n" t
threadDelay (10^6 * t)
printf "%d seconds is up! BING!\BEL\n" t
Concurrent[4]:
import Control.Concurrent
import Text.Printf
import Control.Monad
main = loop
where
loop = do
s <- getLine
if s == "exit"
then return ()
else do forkIO $ setReminder s
loop
setReminder :: String -> IO ()
setReminder s = do
let t = read s :: Int
printf "Ok, I'll remind you in %d seconds\n" t
threadDelay (10^6 * t)
printf "%d seconds is up! BING!\BEL\n" t