-
Notifications
You must be signed in to change notification settings - Fork 63
/
Field.hs
185 lines (145 loc) · 4.72 KB
/
Field.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
module Plutarch.Field
(HList (..), indexHList, Elem (..), NatElem (..), getField, BindFields (..), xssum) where
--import Data.Kind (Type)
--import Data.Proxy (Proxy (..))
import GHC.TypeLits (type (-), type (+), Nat)
import Plutarch.DataRepr
(PDataList, pdhead, pdtail)
import Plutarch.Internal (TermCont (..), punsafeCoerce)
--import Plutarch (PType)
import Plutarch.Builtin (PAsData, PData, PBuiltinList, PIsData (..))
import Plutarch.Prelude
import qualified PlutusTx
import Plutarch.Integer
import Plutarch.Lift
--------------------------------------------------------------------------------
-- | Usual GADT Heterogenous List encoding
data HList (xs :: [Type]) where
HNil :: HList '[]
HCons :: x -> HList xs -> HList (x ': xs)
-- | GADT proof-witness of HList membership, usable as an index
data Elem (x :: k) (xs :: [k]) where
Here :: Elem x (x ': xs)
There :: Elem x xs -> Elem x (y ': xs)
-- | Index HList using Elem
indexHList :: HList xs -> (forall x. Elem x xs -> x)
indexHList (HCons x _) Here = x
indexHList (HCons _ xs) (There i) = indexHList xs i
indexHList HNil impossible = case impossible of {}
-- | Indexing type-level lists
type family IndexList (n :: Nat) (l :: [k]) :: k where
IndexList 0 (x ': _) = x
IndexList n (x : xs) = IndexList (n - 1) xs
{- |
Construct an `Elem` via Nat.
This class could instead be a more direct version of 'indexHList',
but perhaps the `Elem` encoding will be useful.
-}
class
(IndexList n xs ~ x) =>
NatElem (n :: Nat) (x :: k) (xs :: [k]) | xs n -> x where
{- | Construct the `Elem` corresponding to a Nat index.
Example:
>>> natElem @_ @0
Here
>>> natElem @_ @3
There (There (There Here))
-}
natElem :: Elem x xs
instance {-# OVERLAPS #-} NatElem 0 x (x ': xs) where
natElem :: Elem x (x ': xs)
natElem = Here
instance {-# OVERLAPPABLE #-}
( IndexList n (y ': xs) ~ x
, NatElem (n - 1) x xs
) =>
NatElem n x (y ': xs) where
natElem :: Elem x (y ': xs)
natElem = There (natElem @_ @(n-1) @x @xs)
-- | Get the Index of a type in a list
type family IndexOf (x :: k) (xs :: [k]) :: Nat where
IndexOf x (x ': _) = 0
IndexOf x (y ': xs) = (IndexOf x xs + 1)
{- |
Construct an Elem via the position of a given type
in a type-level list.
The intended use is to 'lookup' the index of a
field name:
>>> fieldElem @"z" @["x", "y", "z", "w"]
>>> There (There Here))
-}
fieldElem ::
forall f fs x xs n.
( (IndexOf f fs ~ n)
, (IndexList n xs) ~ x
, NatElem n x xs
) =>
Elem x xs
fieldElem = natElem @_ @n
{- |
Index a HList with a field in a provided list of fields.
>>> xs = HCons 1 (HCons 2 (HCons 3 HNil))
>>> getField @"y" @["x", "y", "z"] xs
>>> 2
-}
getField
:: forall f fs x xs n.
( (IndexOf f fs ~ n)
, (IndexList n xs) ~ x
, NatElem n x xs
) =>
HList xs -> x
getField xs = indexHList xs $ fieldElem @f @fs
--------------------------------------------------------------------------------
type family TermsOf (s :: S) (as :: [PType]) :: [Type] where
TermsOf _ '[] = '[]
TermsOf s (x ': xs) = Term s (PAsData x) ': TermsOf s xs
class BindFields as where
{- |
Bind all the fields in a 'PDataList' term to a corresponding
HList of Terms.
A continuation is returned to enable sharing of
the generated bound-variables.
-}
bindFields' :: Term s (PDataList as) -> TermCont s (HList (TermsOf s as))
instance {-# OVERLAPS #-}
BindFields (a ': '[]) where
bindFields' t =
pure $ HCons (pdhead # t) HNil
instance {-# OVERLAPPABLE #-}
(BindFields as) => BindFields (a ': as) where
bindFields' t = do
t' <- TermCont $ plet t
--tail <- TermCont $ plet $ pdtail # t
xs <- bindFields @as (pdtail # t')
pure $ HCons (pdhead # t') xs
xs :: Term s (PDataList '[PInteger, PInteger, PInteger])
xs =
punsafeCoerce $
pconstant @(PBuiltinList PData) $ fmap (PlutusTx.toData @Integer) [1,2,3]
xssum :: Term s PInteger
xssum =
runTermCont (bindFields' xs) $ \case
(HCons x (HCons y (HCons z HNil))) ->
pfromData x + pfromData y + pfromData z
--------------------------------------------------------------------------------
{- |
Heterogenous Record, using a list of symbols as a
set of corresponding indices by position.
-}
newtype HRec (fs :: [Symbol]) (as :: [Type]) =
HRec { hrecList :: HList as }
class PDataFields (a :: PType) where
type PFieldNames a :: [Symbol]
type PFieldTypes a :: [PType]
ptoFields :: Term s a -> Term s (PDataList (PFieldTypes a))
letFields ::
( PDataFields a
, BindFields (PFieldTypes a)
) =>
Term s a -> (HRec (PFieldNames a) (PFieldTypes a) -> Term s b) -> Term s b
letFields t =
bindFields @as