can have hundreds of thousands of futures with just a handful of threads! • The thread pools are typically conﬁgured separately. • We do not block current thread while waiting for a result of another future.
the original non- concurrent code. ✴ Invisible, except when it shouldn't be. ✴ Computations expressed as data dependencies, rather than with complex control ﬂow. ✴ Error handling is smooth. ✴ Scales easily. ✴ Cons ✴ … We'll come to that!
having cleaner syntax for callback-based async work. They are about modelling your problem in a fundamentally different way; they go deeper than syntax and actually change the way you solve problems at a semantic level."
-> b) -> b map :: Seq a -> (a -> b) -> Seq b tryCatch :: (() -> a) -> (() -> a) -> a filter :: Seq a -> (a -> Bool) -> Seq a // Future-based code: (<-) :: Future a -> (a -> Future b) -> Future b traverse :: Seq a -> (a -> Future b) -> Future (Seq b) recover :: Future a -> (() -> a) -> Future a filterF :: Seq a -> (a -> Future Bool) -> Future (Seq a) Types
• ThreadLocal variables. • Complex co-ordination not very natural. • Simpler than other concurrency models, but still some cognitive cost. Could be better. • Not suitable for all sorts of concurrency scenarios.