• 作成:

NoFieldSelectorsを有効にするとmakeFieldsが使えなくなる問題のワークアラウンド

前提

NoFieldSelectors を使うと、一々フィールドにコンストラクタのプレフィクスを付けなくても良くなります。

しかしlensmakeFieldsなどは、コンストラクタのプレフィクスか_でフィールドが始まることを期待しているため、プレフィクスを無くしたらアクセサを作ってくれなくなります。

そもそもNoFieldSelectorsがあればlens要らないのではとか思わなくはないですが、まだRecord Dot Syntaxでは、ネストしたデータを更新する実装が自動で生えてくるわけではなさそうなこと。複数の型を跨いだ更新などに便利なこと。 lensには単純に代入する以外のアクセサがあることを考えると、まだまだlensは使っていきたいですね。

よって解決する極めて簡単な関数を書きました。

解決

module Control.Lens.TH.NoFieldSelectors (makeLensesId) where

import           Control.Lens
import qualified Data.Char           as C
import           Language.Haskell.TH

-- | `NoFieldSelectors`を有効にした環境では、
-- フィールドにプレフィクスを付けなくて済みますが、
-- lensがアクセサを作る条件を満たさなくなるため雑に許可します。
makeLensesId :: Name -> DecsQ
makeLensesId = makeLensesWith idRules

idRules :: LensRules
idRules = defaultFieldRules & lensField .~ idNamer

idNamer :: FieldNamer
idNamer _ _ field =
  let fieldPart = nameBase field
      classNamePart = case fieldPart of
        x : xs -> C.toUpper x : xs
        []     -> error "field is empty"
  in [MethodName (mkName $ "Has" ++ classNamePart) (mkName fieldPart)]

このmakeLensesIdmakeLensesの変わりに使えば完了です。しかしGHC 9.2の動きも考えると、 lensのデフォルト動作でアクセサを作っても良い気がしますね。そのへんも含めてlensにissueを建てることにします。