st (md5sum2, jv) | md5sum == md5sum2 -> do
            return jv

          _ -> do
            new_jv <- either (liftIO . throwIO . YamlFileParseException full_path) return $ Y.decodeEither' bs
            st SV.$~! (HM.insert full_path (md5sum, new_jv))
            return new_jv

  case A.fromJSON jv of
    A.Error err -> liftIO $ throwIO $ YamlFileParseException full_path (Y.AesonException err)
    A.Success x -> return x


-- | May throw IOError
runDelayedCachedYamlLoaderL_IOE :: ( MonadIO m)
                                => s
                                -> NonEmpty FilePath    -- ^ 消息文件存放目录
                                -> DelayedCachedYamlLoader s a
                                -> m (Either YamlFileParseException a)
runDelayedCachedYamlLoaderL_IOE st base_dirs f = liftIO $ try $ runReaderT (f base_dirs) st

-- | Don't throw IOError
runDelayedCachedYamlLoaderL :: ( MonadIO m )
                            => s
                            -> NonEmpty FilePath    -- ^ 消息文件存放目录
                            -> DelayedCachedYamlLoader
mkDelayedCachedYamlLoader :: ( FromJSON a, CachedYamlInfoState s)
                          => FilePath
                          -> DelayedCachedYamlLoader s a
mkDelayedCachedYamlLoader fp base_dir_list = do
  full_path <- liftIO (findFirstMatchedFile base_dir_list fp)
                >>= maybe
                    (liftIO $ throwIO $ mkIOError doesNotExistErrorType "mkDelayedCachedYamlLoader" Nothing (Just fp))
                    return

  bs <- liftIO $ B.readFile full_path
  let md5sum = md5HashBS bs

  st <- ask
  cache_map <- SV.get st
  jv <- case HM.lookup full_path cache_map of
          Just (md5sum2, jv) | md5sum == md5sum2 -> do
            return jv

          _ -> do
            new_jv <- either (liftIO . throwIO . YamlFileParseException full_path) return $ Y.decodeEither' bs
            st SV.$~! (HM.insert full_path (md5sum, new_jv))
            return new_jv

  case A.fromJSON jv of
    A.Error err -> liftIO $ throwIO $ YamlFileParseException full_path (Y.AesonException err)
    A.Success x -> return x


-- | May throw IOError
mkDelayedCachedYamlLoader fp base_dir_list = do
  full_path <- liftIO (findFirstMatchedFile base_dir_list fp)
                >>= maybe
                    (liftIO $ throwIO $ mkIOError doesNotExistErrorType "mkDelayedCachedYamlLoader" Nothing (Just fp))
                    return

  bs <- liftIO $ B.readFile full_path
  let md5sum = md5HashBS bs

  st <- ask
  cache_map <- SV.get st
  jv <- case HM.lookup full_path cache_map of
          Just (md5sum2, jv) | md5sum == md5sum2 -> do
            return jv

          _ -> do
            new_jv <- either (liftIO . throwIO . YamlFileParseException full_path) return $ Y.decodeEither' bs
            st SV.$~! (HM.insert full_path (md5sum, new_jv))
            return new_jv

  case A.fromJSON jv of
    A.Error err -> liftIO $ throwIO $ YamlFileParseException full_path (Y.AesonException err)
    A.Success x -> return x


-- | May throw IOError