Skip to content

Commit

Permalink
chore: refactor asDataFor, remove long lines, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
Unisay committed Mar 26, 2024
1 parent 2c41ace commit ac79a71
Showing 1 changed file with 48 additions and 36 deletions.
84 changes: 48 additions & 36 deletions plutus-tx/src/PlutusTx/AsData.hs
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
-- editorconfig-checker-disable-file
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}

module PlutusTx.AsData (asData, asDataFor) where

import Control.Lens (ifor)
import Control.Monad (unless)
import Data.Foldable
import Data.Traversable
import Data.Traversable (for)

import Language.Haskell.TH qualified as TH
import Language.Haskell.TH.Datatype qualified as TH
import Language.Haskell.TH.Datatype.TyVarBndr qualified as TH

import PlutusTx.Builtins as Builtins
import PlutusTx.IsData.Class
import PlutusTx.Builtins qualified as Builtins
import PlutusTx.IsData.Class (ToData, UnsafeFromData)
import PlutusTx.IsData.TH (mkConstrCreateExpr, mkUnsafeConstrMatchPattern)

import Prelude
Expand All @@ -41,26 +40,28 @@ declaration. Note that you may therefore need to do strange things like use
Example:
@
$(asData
[d|
data Example a = Ex1 Integer | Ex2 a a
deriving newtype (Eq)
|]
$(asData [d|
data Example a = Ex1 Integer | Ex2 a a
deriving newtype (Eq)
|])
@
becomes
@
newtype Example a = Example BuiltinData
deriving newtype (Eq)
newtype Example a = Example BuiltinData
deriving newtype (Eq)
pattern Ex1 :: (ToData a, UnsafeFromData a) => Integer -> Example a
pattern Ex1 i <- Example (unsafeDataAsConstr -> ((==) 0 -> True, [unsafeFromBuiltinData -> i]))
where Ex1 i = Example (mkConstr 0 [toBuiltinData i])
pattern Ex1 :: (ToData a, UnsafeFromData a) => Integer -> Example a
pattern Ex1 i <- Example (unsafeDataAsConstr -> ((==) 0 -> True, [unsafeFromBuiltinData -> i]))
where Ex1 i = Example (mkConstr 0 [toBuiltinData i])
pattern Ex2 :: (ToData a, UnsafeFromData a) => a -> a -> Example a
pattern Ex2 a1 a2 <- Example (unsafeDataAsConstr -> ((==) 1 -> True, [unsafeFromBuiltinData -> a1, unsafeFromBuiltinData -> a2]))
where Ex2 a1 a2 = Example (mkConstr 1 [toBuiltinData a1, toBuiltinData a2])
pattern Ex2 :: (ToData a, UnsafeFromData a) => a -> a -> Example a
pattern Ex2 a1 a2 <- Example (unsafeDataAsConstr -> ((==) 1 -> True,
[ unsafeFromBuiltinData -> a1
, unsafeFromBuiltinData -> a2
]))
where Ex2 a1 a2 = Example (mkConstr 1 [toBuiltinData a1, toBuiltinData a2])
{-# COMPLETE Ex1, Ex2 #-}
{-# COMPLETE Ex1, Ex2 #-}
@
-}
asData :: TH.Q [TH.Dec] -> TH.Q [TH.Dec]
Expand All @@ -76,15 +77,27 @@ asDataFor dec = do
TH.DataD _ _ _ _ _ deriv -> deriv
_ -> []

di@(TH.DatatypeInfo{TH.datatypeVariant=dVariant, TH.datatypeCons=cons, TH.datatypeName=name, TH.datatypeVars=tTypeVars}) <- TH.normalizeDec dec
di@(
TH.DatatypeInfo
{ TH.datatypeVariant = dVariant
, TH.datatypeCons = cons
, TH.datatypeName = name
, TH.datatypeVars = tTypeVars
}
) <- TH.normalizeDec dec

-- Other stuff is data families and so on
unless (dVariant == TH.Datatype) $ fail $ "asData: can't handle datatype variant " ++ show dVariant
unless (dVariant == TH.Datatype) $
fail $ "asData: can't handle datatype variant " ++ show dVariant
-- a fresh name for the new datatype, but same lexically as the old one
cname <- TH.newName (show name)
-- The newtype declaration
let ntD =
let con = TH.NormalC cname [(TH.Bang TH.NoSourceUnpackedness TH.NoSourceStrictness, TH.ConT ''BuiltinData)]
let con = TH.NormalC cname
[ ( TH.Bang TH.NoSourceUnpackedness TH.NoSourceStrictness
, TH.ConT ''Builtins.BuiltinData
)
]
in TH.NewtypeD [] name
#if MIN_VERSION_template_haskell(2,21,0)
(TH.changeTVFlags TH.BndrReq tTypeVars)
Expand All @@ -94,36 +107,35 @@ asDataFor dec = do
Nothing con derivs

-- The pattern synonyms, one for each constructor
pats <- ifor cons $ \conIx (TH.ConstructorInfo{TH.constructorName=conName, TH.constructorFields=fields, TH.constructorVariant=cVariant}) -> do
pats <- ifor cons $
\conIx TH.ConstructorInfo
{ TH.constructorName = conName
, TH.constructorFields = fields
, TH.constructorVariant = cVariant
} -> do
-- If we have a record constructor, we need to reuse the names for the
-- matching part of the pattern synonym
fieldNames <- case cVariant of
TH.RecordConstructor names -> pure names
-- otherwise whatever
_ -> ifor fields $ \fieldIx _ -> TH.newName $ "arg" ++ show fieldIx
let extractFieldNames = fieldNames
createFieldNames <- for fieldNames (TH.newName . show)
patSynArgs <- case cVariant of
TH.NormalConstructor -> pure $ TH.prefixPatSyn extractFieldNames
TH.RecordConstructor _ -> pure $ TH.recordPatSyn extractFieldNames
TH.InfixConstructor -> case extractFieldNames of
TH.NormalConstructor -> pure $ TH.prefixPatSyn fieldNames
TH.RecordConstructor _ -> pure $ TH.recordPatSyn fieldNames
TH.InfixConstructor -> case fieldNames of
[f1,f2] -> pure $ TH.infixPatSyn f1 f2
_ -> fail "asData: infix data constructor with other than two fields"
let
pat = TH.conP cname [mkUnsafeConstrMatchPattern (fromIntegral conIx) fieldNames]

pat = TH.conP cname [mkUnsafeConstrMatchPattern (fromIntegral conIx) extractFieldNames]

createExpr = [| $(TH.conE cname) $(mkConstrCreateExpr (fromIntegral conIx) createFieldNames) |]
createExpr = [|$(TH.conE cname) $(mkConstrCreateExpr (fromIntegral conIx) createFieldNames) |]
clause = TH.clause (fmap TH.varP createFieldNames) (TH.normalB createExpr) []
patSynD = TH.patSynD conName patSynArgs (TH.explBidir [clause]) pat

let
dataConstraints t = [TH.ConT ''ToData `TH.AppT` t, TH.ConT ''UnsafeFromData `TH.AppT` t]
ctxFor vars = concatMap (dataConstraints . TH.VarT . TH.tvName) vars
-- Try and be a little clever and only add constraints on the variables
-- used in the arguments
-- Try and be a little clever and only add constraints on the variables used in the arguments
varsInArgs = TH.freeVariablesWellScoped fields
ctxForArgs = ctxFor varsInArgs
ctxForArgs = concatMap (dataConstraints . TH.VarT . TH.tvName) varsInArgs
conTy = foldr (\ty acc -> TH.ArrowT `TH.AppT` ty `TH.AppT` acc) (TH.datatypeType di) fields
allFreeVars = TH.freeVariablesWellScoped [conTy]
fullTy = TH.ForallT (TH.changeTVFlags TH.SpecifiedSpec allFreeVars) ctxForArgs conTy
Expand Down

0 comments on commit ac79a71

Please sign in to comment.