This post is substantially directed to non Haskellers. Haskell frequently appears on or /r/programming but the content is commonly evangelizing some aspect of functional programming, strong types, and purity. hackernews Haskell embodies all those things, but the practicality does not come from strongly typed functional programming, it comes from the power of the runtime. Other strongly typed functional languages such as exist, but many aspects are . Here I explore some of runtime properties that greatly contribute to the Haskell's power, performance, and convenience. OCaml not nearly as mature Numerous Haskell implementation exist ( , , ) however we will specifically go over which is by far the most used, and powerful. FLRC UHC JHC GHC Asynchronous Exceptions In almost all languages exceptions are thrown by the executing thread. In throwing an exception would look something like this: C++ { ( == ) { ::runtime_error( ); } } { doAction(); } ( ::exception& err) { << err.what() << :: ; } void doAction () if 1 0 throw std "the impossible happened" try catch const std cout std endl In Haskell *any* thread can throw an exception to another thread. Let's spawn a thread and then immediately kill it by throwing an exception from outside of it. No modification of the thread or function itself is required. <- forkIO myLongRunningAction threadId threadId throwTo MyException This capability ends up being incredibly powerful. All of a sudden, features that might normally be implemented as fundamental properties of the language can now be expressed within it. An example of this is . Timeout takes a timeout in microseconds, an action to run, and may or may not return a result depending on if the action completes in time. Internally it spawns a thread with a timer, and should a deadline be hit issues an exception to the other threads computation. timeout :: Int -> IO a -> IO (Maybe a) In a language like a goroutine cannot be killed externally. A common pattern emerges where authors manually wait on channels to ensure can be controlled: Go stop := ( ) { { { <- stop: } } }() stop <- make chan bool go func () for select case return true Should a mistake be made resources will leak, and more complicated control flows must be continually duplicated. Even killing a thread utilizing owned resources such as sockets is safe in Haskell because of constructs built on that automatically cleanup resources. bracket Automatic detection of failure cases is hard. Language features to make screwing up harder . While Haskell will not stop you from deadlocking your program, the runtime has tooling to detect when this happens and throw an exception such as in the deadlocked code. Fearless concurrency are in the works BlockedIndefinitelyOnMVar Haskell is so while less common, if an unproductive infinite loop is detected a exception may be thrown. This is however . lazy NonTermination not perfect Haskell has limiting required stack size however a recoverable can be thrown. Various arithmetic exceptions exist for numeric errors such as , or . tail call optimization StackOverflow Overflow DivideByZero Each thread can have independently set allocation limits via which causes an exception. This can be useful for handling . setAllocationCounter AllocationLimitExceeded multitenancy Green threads and asynchronous networking Most languages have some implementation of , but few have it as the primary mode of computation. The success of , and for writing networked applications is heavily tied to this model of concurrency. Languages such as that implement the functionality as a library, have . green threads Go Erlang Rust substantial ergonomics implications Standard POSIX consume substantially more resources than green threads, and utilize the operating systems scheduler. On my system: pthreads > -a | grep stack stack size (kbytes, -s) 8192 ulimit the default thread stack size as 8 megabytes. Technically Linux does lazy allocation via virtual memory, however even *spawning and killing* a thread can consume . thousands of CPU cycles Scaling to a large number of concurrent users (generally known as the ), is usually accomplished via asynchronous IO operations. Asynchronous IO multiplexing combined with a low level of parallelism is how services such as attain such high performance. C10K problem NGINX GHC internally utilizes epoll, or kqueue depending on platform. And support for the new io_uring Linux API is being , which can bring . experimented with already substantial performance gains Of green thread implementations, Haskell's is among the most powerful. For example with you can inspect the status of a thread, and if blocked even see *why*: threadStatus :: ThreadId -> IO ThreadStatus = | | | | | data BlockReason BlockedOnMVar -- ^blocked on 'MVar' BlockedOnBlackHole -- ^blocked on a computation in progress by another thread BlockedOnException -- ^blocked in 'throwTo' BlockedOnSTM -- ^blocked in 'retry' in an STM transaction BlockedOnForeignCall -- ^currently in a foreign call BlockedOnOther Performance tuning and GC Some languages such as Java are famous for the . A garbage collector cannot be perfect for every workload. There exist among others. tuning capabilities throughput and latency tradeoffs By default, a generational copying collector is used by the runtime. GHC has a wide variety of . One of particular interest is to enable optimizations for high core count multi CPU servers which have higher communication overhead. flags to flip, and knobs to turn --numa Profiling and related tooling Haskell has a full range of tooling to support debugging and building complex applications: Code coverage reporting via HCP. Time profiling flamegraphs via ghc-prof-flamegraph Space profiling graphs via hp2ps Also published on: https://harporoeder.com/posts/haskell-runtime/