Skip to content

Commit

Permalink
fixup! [#15] Add lenses for config-related types
Browse files Browse the repository at this point in the history
  • Loading branch information
nalkuatov committed Jul 5, 2022
1 parent a093735 commit 76c486b
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 98 deletions.
3 changes: 1 addition & 2 deletions core/nyan-interpolation-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ library
Text.Interpolation.Nyan.Core.Internal.Processor
Text.Interpolation.Nyan.Core.Internal.RMode
Text.Interpolation.Nyan.Core.Internal.Splice
Text.Interpolation.Nyan.Lens
Text.Interpolation.Nyan.Lens.TH
Text.Interpolation.Nyan.Lens.Type
Text.Interpolation.Nyan.RModes.Buildable
Text.Interpolation.Nyan.RModes.CommonExtra
Text.Interpolation.Nyan.RModes.Show
Expand Down Expand Up @@ -142,6 +140,7 @@ test-suite nyan-interpolation-core-tests
, base <4.17
, fmt
, megaparsec
, microlens
, mtl
, nyan-interpolation-core
, tasty
Expand Down
1 change: 1 addition & 0 deletions core/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ tests:
<<: *test-common
dependencies:
- HUnit
- microlens
- nyan-interpolation-core
- tasty
- tasty-hunit-compat
2 changes: 1 addition & 1 deletion core/src/Text/Interpolation/Nyan/Core/Internal/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Data.Monoid (Endo (..))
import Data.Text (Text)
import Language.Haskell.TH (ExpQ)

import Text.Interpolation.Nyan.Lens (makeLenses)
import Text.Interpolation.Nyan.Lens.TH (makeLenses)

{- $setup
Expand Down
52 changes: 0 additions & 52 deletions core/src/Text/Interpolation/Nyan/Lens.hs

This file was deleted.

39 changes: 28 additions & 11 deletions core/src/Text/Interpolation/Nyan/Lens/TH.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ module Text.Interpolation.Nyan.Lens.TH
)
where

import Control.Monad ((<=<))
import Control.Monad (forM)
import Language.Haskell.TH

-- | Information about the record field the lenses will operate on.
type RecordFieldInfo = (Name, Strict, Type)

-- | Given a record datatype, derives lenses for all of its fields.
makeLenses :: Name -> Q [Dec]
makeLenses = mapM deriveLens <=< extractRecordFields
makeLenses datatype = do
fields <- extractRecordFields datatype
fmap concat $ forM fields \field -> do
sig <- deriveLensSignature datatype field
body <- deriveLensBody field
return [sig, body]

extractRecordFields :: Name -> Q [RecordFieldInfo]
extractRecordFields datatype = do
Expand All @@ -25,29 +30,41 @@ extractRecordFields datatype = do
TyConI (DataD _ _ _ _ [RecC _ fs] _) -> fs
TyConI (NewtypeD _ _ _ _ (RecC _ fs) _) -> fs
TyConI (DataD _ _ _ _ [_] _) ->
error $ "Can't derive lenses without record selectors: " ++ datatypeStr
fail $ "Can't derive lenses without record selectors: " ++ datatypeStr
TyConI NewtypeD{} ->
error $ "Can't derive lenses without record selectors: " ++ datatypeStr
fail $ "Can't derive lenses without record selectors: " ++ datatypeStr
TyConI TySynD{} ->
error $ "Can't derive lenses for type synonym: " ++ datatypeStr
fail $ "Can't derive lenses for type synonym: " ++ datatypeStr
TyConI DataD{} ->
error $ "Can't derive lenses for a sum type: " ++ datatypeStr
fail $ "Can't derive lenses for a sum type: " ++ datatypeStr
_ ->
error $ "Can't derive lenses for: " ++ datatypeStr
fail $ "Can't derive lenses for: " ++ datatypeStr
++ ", type name required."

mkLensName :: Name -> Name
mkLensName = mkName . (<> "L") . nameBase

deriveLensSignature :: Name -> RecordFieldInfo -> Q Dec
deriveLensSignature datatype (fieldName, _, fieldType) =
sigD (mkLensName fieldName)
[t|forall f. Functor f => ($field -> f $field)
-> $record -> f $record
|]
where
field = return fieldType
record = conT datatype

-- | Given a record field name,
-- produces a single function declaration:
-- @lensName f a = (\x -> a { field = x }) `fmap` f (field a)@
deriveLens :: RecordFieldInfo -> Q Dec
deriveLens (fieldName, _, _) = funD lensName [defLine]
deriveLensBody :: RecordFieldInfo -> Q Dec
deriveLensBody (fieldName, _, _) = funD (mkLensName fieldName) [defLine]
where
lensName = mkName $ (nameBase fieldName) <> "L"
a = mkName "a"
f = mkName "f"
defLine = clause pats (normalB body) []
pats = [varP f, varP a]
body = [| (\x -> $(record a fieldName [|x|]))
`fmap` $(appE (varE f) (appE (varE fieldName) (varE a)))
`fmap` $(varE f `appE` (varE fieldName `appE` varE a))
|]
record rec fld val = val >>= \v -> recUpdE (varE rec) [return (fld, v)]
17 changes: 0 additions & 17 deletions core/src/Text/Interpolation/Nyan/Lens/Type.hs

This file was deleted.

17 changes: 2 additions & 15 deletions core/tests/Test/Lens.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

module Test.Lens where

import Lens.Micro
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@?=))

import Text.Interpolation.Nyan.Lens
import Text.Interpolation.Nyan.Lens.TH (makeLenses)

data Pair = Pair { first :: Int, second :: String }
deriving stock (Show, Eq)
Expand Down Expand Up @@ -38,20 +39,6 @@ test_makeLenses = testGroup "Lenses produced by 'makeLenses' work as expected"
single & (valueL ?~ "Some value")
@?= Single (Just "Some value")
]

, testGroup "Operators leveraging 'MonadState', 'State' work as expected"
[ testCase "(&~) and (.=) work" do
(pair &~ do firstL .= 102)
@?= Pair 102 "Hundred"

, testCase "(&~) and (?=) work" do
(single &~ do valueL ?= "Some value")
@?= Single (Just "Some value")

, testCase "(&~) and (%=) work" do
(single &~ do valueL %= (const $ Just "Some value"))
@?= Single (Just "Some value")
]
]
where
a & f = f a
Expand Down

0 comments on commit 76c486b

Please sign in to comment.