@@ -0,0 +1,177 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TypeApplications #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeOperators #-}
12+{-# LANGUAGE UndecidableInstances #-}
14+-----------------------------------------------------------------------------
16+-- Module : Data.Generics.Record.HasField
17+-- Copyright : (C) 2017 Csongor Kiss
19+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20+-- Stability : experimental
21+-- Portability : non-portable
23+-- Derive positional product type getters and setters generically.
25+-----------------------------------------------------------------------------
27+module Data.Generics.Product.Positions
39+import Data.Generics.Internal.Lens
41+import Data.Kind (Constraint, Type)
47+-- module Example where
49+-- import Data.Generics.Product
50+-- import GHC.Generics
55+-- , address :: String
57+-- deriving (Generic, Show)
60+-- human = Human \"Tunyasz\" 50 \"London\"
63+-- | Get positional field
65+-- >>> getPosition @1 human
67+getPosition :: forall i a s. HasPosition i a s => s -> a
68+getPosition s = s ^. position @i
70+-- | Set positional field
72+-- >>> setPosition @2 (setField @1 "Tamas" human) 30
73+-- Human "Tamas" 30 "London"
74+setPosition :: forall i a s. HasPosition i a s => a -> s -> s
75+setPosition = set (position @i)
77+-- |Records that have a field at a given position.
78+class HasPosition (i :: Nat) a s | s i -> a where
79+ -- |A lens that focuses on a field at a given position. Compatible with the
80+ -- lens package's 'Control.Lens.Lens' type.
82+ -- >>> human ^. position @1
84+ -- >>> human & position @2 .~ "Berlin"
85+ -- Human {name = "Tunyasz", age = 50, address = "Berlin"}
86+ position :: Lens' s a
90+ , ErrorUnlessTrue i s (0 <? i && i <=? Size (Rep s))
91+ , GHasPosition 1 i (Rep s) a
92+ ) => HasPosition i a s where
94+ position = repIso . gposition @1 @i
96+type family ErrorUnlessTrue (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
97+ ErrorUnlessTrue i s 'False
101+ ':<>: 'Text " does not contain a field at position "
105+ ErrorUnlessTrue _ _ 'True
108+-- |As 'HasPosition' but over generic representations as defined by
110+class GHasPosition (offset :: Nat) (i :: Nat) (f :: Type -> Type) a | offset i f -> a where
111+ gposition :: Lens' (f x) a
113+instance GHasPosition i i (S1 meta (Rec0 a)) a where
114+ gposition = mIso . gposition @i @i
116+instance GHasPosition offset i (K1 R a) a where
117+ gposition f (K1 x) = fmap K1 (f x)
119+instance GHasPosition offset i f a => GHasPosition offset i (M1 D meta f) a where
120+ gposition = mIso . gposition @offset @i
122+instance GHasPosition offset i f a => GHasPosition offset i (M1 C meta f) a where
123+ gposition = mIso . gposition @offset @i
126+ ( goLeft ~ (i <? (offset + Size l))
127+ , offset' ~ (If goLeft offset (offset + Size l))
128+ , GProductHasPosition offset' i l r a goLeft
129+ ) => GHasPosition offset i (l :*: r) a where
131+ gposition = gproductPosition @offset' @i @_ @_ @_ @goLeft
134+class GProductHasPosition (offset :: Nat) (i :: Nat) l r a (left :: Bool) | offset i l r left -> a where
135+ gproductPosition :: Lens' ((l :*: r) x) a
137+instance GHasPosition offset i l a => GProductHasPosition offset i l r a 'True where
138+ gproductPosition = first . gposition @offset @i
140+instance GHasPosition offset i r a => GProductHasPosition offset i l r a 'False where
141+ gproductPosition = second . gposition @offset @i
143+type family Size f :: Nat where
155+--------------------------------------------------------------------------------
157+type family Not (b :: Bool) where
161+type family (<?) (x :: Nat) (y :: Nat) :: Bool where
162+ x <? y = Not (y <=? x)
166+type family If (c :: Bool) (x :: k) (y :: k) :: k where
170+type family Min (a :: Nat) (b :: Nat) :: Nat where
171+ Min a b = If (a <? b) a b
173+type family (a :: Bool) && (b :: Bool) :: Bool where
174+ 'True && 'True = 'True