Skip to content

Commit

Permalink
Add OverloadedLabels support for positional lenses
Browse files Browse the repository at this point in the history
  • Loading branch information
amesgen committed Apr 18, 2023
1 parent 4b34e49 commit f7ef335
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
51 changes: 45 additions & 6 deletions generic-lens/src/Data/Generics/Labels.hs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
#if MIN_VERSION_base(4,12,0)
{-# LANGUAGE NoStarIsType #-}
#endif
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
Expand Down Expand Up @@ -107,15 +111,18 @@ instance {-# INCOHERENT #-} AsConstructor name s t a b => Constructor name s t a
instance {-# INCOHERENT #-} AsConstructor' name s a => Constructor name s s a a where
constructorPrism = _Ctor' @name

data LabelType = FieldType | LegacyConstrType | ConstrType
data LabelType = FieldType | LegacyConstrType | ConstrType | PositionType

type family ClassifyLabel (name :: Symbol) :: LabelType where
ClassifyLabel name =
If (CmpSymbol "_@" name == 'LT && CmpSymbol "_[" name == 'GT)
'LegacyConstrType
( If (CmpSymbol "@" name == 'LT && CmpSymbol "[" name == 'GT)
'ConstrType
'FieldType
If (StartsWithDigit name)
'PositionType
( If (CmpSymbol "_@" name == 'LT && CmpSymbol "_[" name == 'GT)
'LegacyConstrType
( If (CmpSymbol "@" name == 'LT && CmpSymbol "[" name == 'GT)
'ConstrType
'FieldType
)
)

instance ( labelType ~ ClassifyLabel name
Expand Down Expand Up @@ -144,3 +151,35 @@ instance ( Applicative f, Choice p, Constructor name s t a b
instance ( Applicative f, Choice p, Constructor name s t a b
) => IsLabelHelper 'ConstrType name p f s t a b where
labelOutput = constructorPrism @name

#if MIN_VERSION_base(4,18,0)
type StartsWithDigit name =
CmpSymbol "/" name == 'LT && CmpSymbol ":" name == 'GT

class Position (i :: Nat) s t a b | s i -> a, t i -> b, s i b -> t, t i a -> s where
positionLens :: Lens s t a b

instance {-# INCOHERENT #-} HasPosition i s t a b => Position i s t a b where
positionLens = position @i

instance {-# INCOHERENT #-} HasPosition' i s a => Position i s s a a where
positionLens = position' @i

instance ( Functor f, Position i s t a b, i ~ ParseNat name
) => IsLabelHelper 'PositionType name (->) f s t a b where
labelOutput = positionLens @i

type ParseNat name = ParseNat' 0 (UnconsSymbol name)

type family ParseNat' acc m where
ParseNat' acc ('Just '(hd, tl)) =
ParseNat' (10 * acc + DigitToNat hd) (UnconsSymbol tl)
ParseNat' acc 'Nothing = acc

type DigitToNat c =
If ('0' <=? c && c <=? '9')
(CharToNat c - CharToNat '0')
(TypeError ('Text "Invalid position number"))
#else
type StartsWithDigit name = 'False
#endif
8 changes: 8 additions & 0 deletions generic-lens/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ tests = TestList $ map mkHUnitTest
, (valLabel ^? #RecB . _1 ) ~=? Just 3
, (valLabel ^? #RecB ) ~=? Just (3, True)
, (valLabel ^? #RecC ) ~=? Nothing

, (valLabel ^. #1 ) ~=? 3
, let
i x = x :: Int
largeTuple = (i 1, i 2, i 3, i 4, i 5, i 6, i 7, i 8, i 9, i 10, i 11, i 12, i 13, i 14, i 15)
largeTuple' = (i 1, i 2, i 3, i 4, i 5, i 6, i 7, i 8, i 9, i 10, i 11, i 13, i 13, i 14, i 15)
in
(largeTuple ^. #13, largeTuple & #12 +~ 1) ~=? (13, largeTuple')
#endif
, customTypesTest
]
Expand Down

0 comments on commit f7ef335

Please sign in to comment.