typeclasses / haskell-phrasebook

The Haskell Phrasebook: a quick intro to Haskell via small annotated example programs
https://typeclasses.com/phrasebook
210 stars 22 forks source link

add TQueue/TBQueue example #18

Closed DKurilo closed 4 years ago

DKurilo commented 4 years ago

Fixes #14 I hope it's good enough. In case I did it not clear enough, write me, please.

chris-martin commented 4 years ago

Hey this is an interesting use of a bounded queue! We've done some revision to make this more concise:

{-# LANGUAGE NumericUnderscores #-}

module Main where

import Control.Concurrent
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception.Safe
import System.IO
import System.Random

-- Let's pretend we will make making many requests to some vendor's API,
-- and the vendor may block us if we make too many simultaneous requests.
vendorApiCall :: Int -> IO Int
vendorApiCall n =
  do
    t <- getStdRandom (randomR (500_000, 1_500_000))
    threadDelay t
    return (n * n)

main :: IO ()
main =
  do
    hSetBuffering stdout LineBuffering

    -- This bounded queue can contain only five elements. Each request will
    -- begin by adding an element onto the queue and end by removing an
    -- element from the queue, thus ensuring that no more than five tasks
    -- can ever be running at once.
    bq <- atomically (newTBQueue 5)
    let
      acquire = atomically (writeTBQueue bq ())
      release = atomically (readTBQueue bq)

    -- `forConcurrently_` is just like `for_`, but concurrent.
    forConcurrently_ [1 .. 10] $ \x ->
        bracket_ acquire release $
          do
            putStrLn ("start: " ++ show x)
            result <- vendorApiCall x
            putStrLn ("finish: " ++ show x ++ ", result: " ++ show result)

What do you think, is it still clear enough?

This'll be released under the creative commons CC BY-NC 4.0 license - Please let us know if that's okay and how you'd like to be attributed.

DKurilo commented 4 years ago

@chris-martin , you almost completely change my example, so surely I'm okay with suggested license. :) Currently, working with experienced Haskell developers a lot better for me than any attribution. And thank you for your comment! Personally, I prefer to have as little amount of "magic" (like bracket and forConcurrently) in examples as it's possible. I'm still learning Haskell and I know, "magic" makes examples obscure and it's difficult to understand what exactly happens under the hood. Not sure if a lot of people will check forConcurrently or bracket implementation. But I also know how difficult to find proper using and "best practices" in Haskell. So I really like your changes. Do you want me to update the code? P.S. It would be great to add link to documentation (or at least to hoogle) for each library function in code, so it will be easier to find implementation for forConcurrently_. :) Maybe it will make beginners read it.

chris-martin commented 4 years ago

We can update the code after we merge, either way's fine. Sure we shuffled a lot around but having the starting point to work with is huge :) Is "Dima Kurilo" the name we should attach to it? (we can link to a blog or twitter account or something too if you'd like)

I perpetually struggle with how much "magic" to include, trying to find the right balance between fundamentals and my presumed readers' impatience. Getting familiar with a set of primitives is a real source of comfort, but if we eschew conveniences that are commonplace in other languages, then that can lead authors down a road that makes Haskell seem way harder than other languages in places where it doesn't need to be. The Phrasebook in particular is an intentional departure from our usual impulses and we're trying to err on the side of giving readers quick easy answers, as long as the things we're recommending are still very generally useful (and I believe forConcurrently_ and bracket_ are).

In this case I think it also really comes down to trying to present one small topic at a time; worrying about how to fork and join threads was distracting while what we were trying to think about was queues. The topics 1. forking/joining and the concept of blocking threads; and 2. exception handling; would make great separate pages with more room to show how to do more things from primitives.

DKurilo commented 4 years ago

Great! Thank you. Yes, you can use "Dima Kurilo" and my twitter: https://twitter.com/DimaKurilo . Got it. As someone who uses haskell phrasebook, I like it. Thank you.