Haskell Count Semi Primes
import Control.Monad (forM_, when)
import Control.Monad.ST (runST)
import Data.Array (Array, listArray, (!))
import Data.Array.ST (newArray, readArray, runSTUArray, writeArray)
import Data.Array.Unboxed (UArray)

isPrimeArr :: Int -> UArray Int Bool
isPrimeArr n = runSTUArray $ do
  arr <- newArray (0, n) True
  forM_ [2 .. floor (sqrt (fromIntegral n :: Double))] $ \i -> do
    p <- readArray arr i
    when p $ forM_ [i * i, i * i + i .. n] $ \k -> writeArray arr k False
  return arr

semiPrimeArr :: Int -> UArray Int Bool -> UArray Int Bool
semiPrimeArr n primes = runSTUArray $ do
  arr <- newArray (0, n) False
  forM_ [k | k <- [2 .. floor (sqrt (fromIntegral n :: Double))], primes ! k] $ \k ->
    forM_ (takeWhile (\i -> i * k <= n) [2 ..]) $ \i ->
      when (primes ! i) $ writeArray arr (k * i) True
  return arr

prefixCounts :: Int -> UArray Int Bool -> Array Int Int
prefixCounts n semi =
  listArray (0, n) (scanl1 (+) (0 : [if semi ! i then 1 else 0 | i <- [1 .. n]]))

countSemiPrimes :: Int -> [Int] -> [Int] -> [Int]
countSemiPrimes n p q = [prefix ! qi - prefix ! (pi' - 1) | (pi', qi) <- zip p q]
  where
    primes = isPrimeArr n
    semi   = semiPrimeArr n primes
    prefix = prefixCounts n semi

This precomputes semiprimes and prefix sums so each range query becomes a quick subtraction.