在镜头中自动生成 Has 类型类的文档在哪里?

Where is the documentation for autogeneration of Has typeclasses in lens?

我正在查看此 blog post (also here), and after verifying it ran, it seemed to confirm that lens can generate Has typeclasses, which I take was the implication from the author of the blog. However, I miss where this is described, either in the lens contents or the lens tutorial 中的倒数第二个示例。官方文档之外的任何关于如何完成此操作的解释也将受到欢迎。但似乎这可能只是使用最基本功能时的标准(makeLenses,或者在这种情况下,makeLensesWith)。

复制代码如下:

#!/usr/bin/env stack
-- stack --resolver lts-8.12 script
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
import Control.Concurrent.Async.Lifted.Safe
import Control.Monad.Reader
import Control.Concurrent.STM
import Say
import Control.Lens
import Prelude hiding (log)

data Env = Env
  { envLog :: !(String -> IO ())
  , envBalance :: !(TVar Int)
  }

makeLensesWith camelCaseFields ''Env

modify :: (MonadReader env m, HasBalance env (TVar Int), MonadIO m)
       => (Int -> Int)
       -> m ()
modify f = do
  env <- ask
  liftIO $ atomically $ modifyTVar' (env^.balance) f

logSomething :: (MonadReader env m, HasLog env (String -> IO ()), MonadIO m)
             => String
             -> m ()
logSomething msg = do
  env <- ask
  liftIO $ (env^.log) msg

main :: IO ()
main = do
  ref <- newTVarIO 4
  let env = Env
        { envLog = sayString
        , envBalance = ref
        }
  runReaderT
    (concurrently
      (modify (+ 1))
      (logSomething "Increasing account balance"))
    env
  balance <- readTVarIO ref
  sayString $ "Final balance: " ++ show balance

Field 是词 lens 用来描述每个命名字段一个 class 的模式,允许具有相同字段名称但(可选)不同类型的多个记录。所以 camelCaseFieldsmakeFieldOpticsdefaultFieldRules 都在他们的名字中说他们将生成这些 HasFoo classes,以 lens.

makeClassy 生成classes 命名为Has*,但以数据类型命名,而不是记录字段,并且不遵循不同的模式。

您上面的代码生成以下代码(显示为 -ddump-splices):

    makeLensesWith camelCaseFields ''Env
  ======>
    class HasBalance s a | s -> a where
      balance :: Lens' s a
    instance HasBalance Env (TVar Int) where
      {-# INLINE balance #-}
      balance f_a4eTr (Env x1_a4eTs x2_a4eTt)
        = (fmap (\ y1_a4eTu -> (Env x1_a4eTs) y1_a4eTu)) (f_a4eTr x2_a4eTt)
    class HasLog s a | s -> a where
      log :: Lens' s a
    instance HasLog Env (String -> IO ()) where
      {-# INLINE log #-}
      log f_a4eTx (Env x1_a4eTy x2_a4eTz)
        = (fmap (\ y1_a4eTA -> (Env y1_a4eTA) x2_a4eTz)) (f_a4eTx x1_a4eTy)