content: {-# language FlexibleInstances #-}
  {-# language GADTs #-}
  {-# language StandaloneDeriving #-}
  {-# language TemplateHaskell #-}
  
  import Control.Monad.IO.Class
  import Data.GADT.Compare.TH (deriveGEq)
  import Data.Hashable
  import Data.Some
  import Data.IORef
  import qualified Rock
  
  data Query a where
    A :: Query Integer
    B :: Query Integer
    C :: Query Integer
    D :: Query Integer
  
  deriving instance Show (Query a)
  deriveGEq ''Query
  
  instance Hashable (Query a) where
    hashWithSalt salt query =
      case query of
        A -> hashWithSalt salt (0 :: Int)
        B -> hashWithSalt salt (1 :: Int)
        C -> hashWithSalt salt (2 :: Int)
        D -> hashWithSalt salt (3 :: Int)
  
  instance Hashable (Some Query) where
    hashWithSalt salt (Some query) = hashWithSalt salt query
  
  rules :: Rock.Rules Query
  rules key = do
    liftIO $ putStrLn $ "Fetching " <> show key
    case key of
      A -> pure 10
      B -> do
        a <- Rock.fetch A
        pure $ a + 20
      C -> do
        a <- Rock.fetch A
        pure $ a + 30
      D ->
        (+) <$> Rock.fetch B <*> Rock.fetch C
  
  main :: IO ()
  main = do
    do
      liftIO $ putStrLn "Running"
      result <- Rock.runTask rules (Rock.fetch D)
      print result
    do
      liftIO $ putStrLn "Running with memoisation"
      memoVar <- newIORef mempty
      result <- Rock.runTask (Rock.memoise memoVar rules) (Rock.fetch D)
      liftIO $ print result
  