hydra-0.15.0: src/main/haskell/Hydra/Dsl/Meta/Terms.hs
{-# LANGUAGE FlexibleContexts #-}
-- | A domain-specific language for constructing term-encoded Hydra terms in Haskell;
-- these functions enable you to build terms (programs) which build terms.
module Hydra.Dsl.Meta.Terms (
module Hydra.Dsl.Meta.Base,
module Hydra.Dsl.Meta.Terms,
) where
import Hydra.Kernel
import qualified Hydra.Dsl.Terms as Terms
import qualified Hydra.Dsl.Meta.Core as Core
import qualified Hydra.Encode.Core as EncodeCore
import Hydra.Dsl.Meta.Base
import qualified Hydra.Dsl.Meta.Phantoms as Phantoms
import qualified Data.ByteString as B
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Scientific as Sci
import qualified Data.Set as S
import qualified Data.Maybe as Y
import Data.Int
import Prelude hiding (map, product, sum)
-- Operators
-- | Function composition operator for term-encoded terms: f <.> g creates a function that applies g then f
-- Example: var "stringLength" <.> var "toString"
(<.>) :: TTerm Term -> TTerm Term -> TTerm Term
f <.> g = compose f g
-- | Function application operator for term-encoded terms
-- Example: fun @@ arg
(@@) :: TTerm Term -> TTerm Term -> TTerm Term
f @@ x = apply f x
-- | Attach an annotation to a term-encoded term
-- Example: annot (Phantoms.map M.empty) (int32 42)
annot :: TTerm (M.Map Name Term) -> TTerm Term -> TTerm Term
annot annMap term = Core.termAnnotated $ Core.annotatedTerm term annMap
-- | Create a term-encoded annotated term (term with type annotations)
-- Example: annotated (int32 42) (Phantoms.map M.empty)
annotated :: TTerm Term -> TTerm (M.Map Name Term) -> TTerm Term
annotated term annMap = Core.termAnnotated $ Core.annotatedTerm term annMap
annotatedTerm :: TTerm Term -> TTerm Term -> TTerm Term
annotatedTerm body ann = inject (Core.nameLift _Term) "annotated" $ record (Core.nameLift _AnnotatedTerm) [
_AnnotatedTerm_body>>: body,
_AnnotatedTerm_annotation>>: ann]
-- | Apply a term-encoded function to a term-encoded argument
-- Example: apply (var "add") (int32 1)
apply :: TTerm Term -> TTerm Term -> TTerm Term
apply func arg = Core.termApplication $ Core.application func arg
-- | Create a term-encoded unlimited precision floating point literal
-- Example: bigfloat 3.14159
bigfloat :: Double -> TTerm Term
bigfloat = bigfloatLift . TTerm . Terms.bigfloat
-- | Lift a TTerm Double to a term-encoded bigfloat literal
bigfloatLift :: TTerm Double -> TTerm Term
bigfloatLift = Core.termLiteral . Core.literalFloat . Core.floatValueBigfloat
-- | Create a term-encoded unlimited precision integer
-- Example: bigint 42
bigint :: Integer -> TTerm Term
bigint = bigintLift . TTerm . Terms.bigint
-- | Lift a TTerm Integer to a term-encoded bigint literal
-- Example: bigintLift (varPhantom "x" :: TTerm Integer)
bigintLift :: TTerm Integer -> TTerm Term
bigintLift = Core.termLiteral . Core.literalInteger . Core.integerValueBigint
-- | Create a term-encoded arbitrary-precision exact decimal literal
-- Example: decimal 42
decimal :: Sci.Scientific -> TTerm Term
decimal = decimalLift . TTerm . Terms.decimal
-- | Lift a TTerm Scientific to a term-encoded decimal literal
decimalLift :: TTerm Sci.Scientific -> TTerm Term
decimalLift = Core.termLiteral . Core.literalDecimal
-- | Create a term-encoded boolean literal
-- Example: boolean True
boolean :: Bool -> TTerm Term
boolean = booleanLift . TTerm . Terms.boolean
-- | Lift a TTerm Bool to a term-encoded boolean literal
-- Example: booleanLift $ Phantoms.true
booleanLift :: TTerm Bool -> TTerm Term
booleanLift = Core.termLiteral . Core.literalBoolean
-- | Create a term-encoded binary (byte string) literal
-- Example: binary (B.pack [0x48, 0x65, 0x6C, 0x6C, 0x6F])
binary :: B.ByteString -> TTerm Term
binary = binaryLift . TTerm . Terms.binary
-- | Lift a TTerm ByteString to a term-encoded binary literal
binaryLift :: TTerm B.ByteString -> TTerm Term
binaryLift = Core.termLiteral . Core.literalBinary
-- | Create a term-encoded constant function that always returns the same term-encoded value
-- Example: constant (int32 42)
constant :: TTerm Term -> TTerm Term
constant = lambda ignoredVariable
-- | Term-encoded boolean false literal
false :: TTerm Term
false = boolean False
-- | Create a term-encoded field with the given name and term-encoded value
-- Example: field "age" (int32 30)
field :: String -> TTerm Term -> TTerm Field
field s = Core.field (name s)
-- | Create a term-encoded 32-bit floating point literal
-- Example: float32 3.14
float32 :: Float -> TTerm Term
float32 = float32Lift . TTerm . Terms.float32
-- | Lift a TTerm Float to a term-encoded float32 literal
-- Example: float32Lift (varPhantom "x" :: TTerm Float)
float32Lift :: TTerm Float -> TTerm Term
float32Lift = Core.termLiteral . Core.literalFloat . Core.floatValueFloat32
-- | Create a term-encoded 64-bit floating point literal
-- Example: float64 3.14159
float64 :: Double -> TTerm Term
float64 = float64Lift . TTerm . Terms.float64
-- | Lift a TTerm Float to a term-encoded float64 literal
-- Example: float64Lift (varPhantom "x" :: TTerm Float)
float64Lift :: TTerm Double -> TTerm Term
float64Lift = Core.termLiteral . Core.literalFloat . Core.floatValueFloat64
-- | Create a term-encoded floating-point literal with specified precision
-- Example: float (FloatValueFloat32 3.14)
float :: TTerm FloatValue -> TTerm Term
float = Core.termLiteral . Core.literalFloat
-- | Create a term-encoded union injection
-- Example: inject (name "Result") "success" (int32 42)
inject :: AsTerm t Name => t -> String -> TTerm Term -> TTerm Term
inject tname fname = Core.termInject . Core.injection (asTerm tname) . Core.field (name fname)
injectUnit :: AsTerm t Name => t -> String -> TTerm Term
injectUnit tname fname = inject tname fname unit
-- | Create a term-encoded 8-bit signed integer literal
-- Example: int8 127
int8 :: Int8 -> TTerm Term
int8 = int8Lift . TTerm . Terms.int8
-- | Lift a TTerm Int8 to a term-encoded int8 literal
-- Example: int8Lift (varPhantom "x" :: TTerm Int8)
int8Lift :: TTerm Int8 -> TTerm Term
int8Lift = Core.termLiteral . Core.literalInteger . Core.integerValueInt8
-- | Create a term-encoded 16-bit signed integer literal
-- Example: int16 32767
int16 :: Int16 -> TTerm Term
int16 = int16Lift . TTerm . Terms.int16
-- | Lift a TTerm Int16 to a term-encoded int16 literal
-- Example: int16Lift (varPhantom "x" :: TTerm Int16)
int16Lift :: TTerm Int16 -> TTerm Term
int16Lift = Core.termLiteral . Core.literalInteger . Core.integerValueInt16
-- | Create a term-encoded 32-bit signed integer literal
-- Example: int32 42
int32 :: Int -> TTerm Term
int32 = int32Lift . TTerm . Terms.int32
-- | Lift a TTerm Int to a term-encoded int32 literal
-- Example: int32Lift (varPhantom "x" :: TTerm Int)
int32Lift :: TTerm Int -> TTerm Term
int32Lift = Core.termLiteral . Core.literalInteger . Core.integerValueInt32
-- | Create a term-encoded 64-bit signed integer literal
-- Example: int64 9223372036854775807
int64 :: Int64 -> TTerm Term
int64 = int64Lift . TTerm . Terms.int64
-- | Lift a TTerm Int64 to a term-encoded int64 literal
-- Example: int64Lift (varPhantom "x" :: TTerm Int64)
int64Lift :: TTerm Int64 -> TTerm Term
int64Lift = Core.termLiteral . Core.literalInteger . Core.integerValueInt64
-- | Create a term-encoded integer literal with specified bit width
-- Example: integer (IntegerValueInt32 42)
integer :: TTerm IntegerValue -> TTerm Term
integer = Core.termLiteral . Core.literalInteger
-- | Create a term-encoded 'Just' optional value
-- Example: just (string "found")
just :: TTerm Term -> TTerm (Maybe Term)
just = Phantoms.just
-- | Create a term-encoded lambda function with one parameter
-- Example: lambda "x" (var "add" @@ var "x" @@ int32 1)
lambda :: String -> TTerm Term -> TTerm Term
lambda var body = Core.termLambda $ Core.lambda (name var) Phantoms.nothing body
-- | Create a term-encoded multi-parameter lambda function (curried form)
-- Example: lambdas ["x", "y"] (var "add" @@ var "x" @@ var "y")
lambdas :: [String] -> TTerm Term -> TTerm Term
lambdas params body = case params of
[] -> body
(h:rest) -> Core.termLambda $ Core.lambda (name h) Phantoms.nothing $ lambdas rest body
-- | Create a term-encoded let expression with multiple bindings
-- Example: lets ["x">: int32 1, "y">: int32 2] (var "add" @@ var "x" @@ var "y")
lets :: [(TTerm Name, TTerm Term)] -> TTerm Term -> TTerm Term
lets pairs body = Core.termLet $ Core.let_ (Phantoms.list $ toBinding pairs) body
where
toBinding = fmap (\(n, t) -> Core.binding n t Phantoms.nothing)
-- | Create a term-encoded left either value
-- Example: left (string "error")
left :: TTerm Term -> TTerm Term
left t = Core.termEither $ Phantoms.left t
-- | Create a term-encoded list
-- Example: list [int32 1, int32 2, int32 3]
list :: [TTerm Term] -> TTerm Term
list = Core.termList . Phantoms.list
-- | Create a term-encoded map/dictionary
-- Example: map (fromList [(string "key", int32 42)])
map :: TTerm (M.Map Term Term) -> TTerm Term
map = Core.termMap
mapTerm1 :: TTerm (M.Map Term Term) -> TTerm Term
mapTerm1 m = inject (Core.nameLift _Term) "map" $ Core.termMap m
-- | Create a term-encoded pattern match on a union
-- Example: match (name "Result") nothing ["success">: int32 42, "error">: string "fail"]
match :: AsTerm t Name => t -> TTerm (Maybe Term) -> [(TTerm Name, TTerm Term)] -> TTerm Term
match tname def pairs = Core.termCases
$ Core.caseStatement (asTerm tname) def $ Phantoms.list $ toField pairs
where
toField = fmap (\(n, t) -> Core.field n t)
--meta :: TTerm a -> TTerm Term
--meta (TTerm term) = TTerm $ EncodeCore.term term
metaref :: TTermDefinition a -> TTerm Term
metaref (TTermDefinition name _) = Core.termVariable $ Core.nameLift name
-- | Create a term-encoded 'Nothing' optional value
nothing :: TTerm (Maybe Term)
nothing = Phantoms.nothing
-- | Create a term-encoded optional value from a Maybe
-- Example: optional (just (int32 42))
optional :: TTerm (Maybe Term) -> TTerm Term
optional = Core.termMaybe
-- | Create a term-encoded pair
-- Example: pair (string "name") (int32 42)
pair :: TTerm Term -> TTerm Term -> TTerm Term
pair t1 t2 = Core.termPair $ Phantoms.pair t1 t2
-- | Create a term-encoded reference to a primitive function.
-- Uses termVariable; the name resolves via graphPrimitives fallthrough.
-- Example: primitive (Name "hydra.lib.strings.length")
primitive :: Name -> TTerm Term
primitive = Core.termVariable . TTerm . EncodeCore.name
-- | Create a term-encoded field projection function
-- Example: project (name "Person") (name "firstName")
project :: (AsTerm t Name, AsTerm f Name) => t -> f -> TTerm Term
project tname fname = Core.termProject
$ Core.projection (asTerm tname) (asTerm fname)
-- | Create a term-encoded record with named fields
-- Example: record (name "Person") ["name">: string "John", "age">: int32 30]
record :: AsTerm t Name => t -> [(TTerm Name, TTerm Term)] -> TTerm Term
record name pairs = Core.termRecord $ Core.record (asTerm name) $ Phantoms.list (toField <$> pairs)
where
toField (n, t) = Core.field n t
recordLift :: Name -> [(TTerm Name, TTerm Term)] -> TTerm Term
recordLift name pairs = record (TTerm $ EncodeCore.name name) pairs
-- | Create a term-encoded right either value
-- Example: right (int32 42)
right :: TTerm Term -> TTerm Term
right t = Core.termEither $ Phantoms.right t
-- | Create a term-encoded set
-- Example: set [string "a", string "b", string "c"]
set :: [TTerm Term] -> TTerm Term
set els = Core.termSet $ TTerm $ TermSet $ S.fromList (unTTerm <$> els)
-- | Create a term-encoded string literal
-- Example: string "hello world"
string :: String -> TTerm Term
string = stringLift . TTerm . Terms.string
-- | Lift a TTerm String to a term-encoded string literal
-- Example: stringLift $ Phantoms.string "hello world"
stringLift :: TTerm String -> TTerm Term
stringLift = Core.termLiteral . Core.literalString
stringTerm :: String -> TTerm Term
stringTerm s = inject (Core.nameLift _Term) "literal" $ inject (Core.nameLift _Literal) "string" $ string s
-- | Create a meta-level term encoding a 16-bit signed integer literal
-- Example: int16Term 100 creates a term that *represents* the int16 literal 100
int16Term :: Int16 -> TTerm Term
int16Term n = inject (Core.nameLift _Term) "literal" $
inject (Core.nameLift _Literal) "integer" $
inject (Core.nameLift _IntegerValue) "int16" $ int16 n
-- | Create a meta-level term encoding a 32-bit signed integer literal
-- Example: int32Term 42 creates a term that *represents* the int32 literal 42
int32Term :: Int -> TTerm Term
int32Term n = inject (Core.nameLift _Term) "literal" $
inject (Core.nameLift _Literal) "integer" $
inject (Core.nameLift _IntegerValue) "int32" $ int32 n
-- | Create a meta-level term encoding a 64-bit signed integer literal
-- Example: int64Term 137 creates a term that *represents* the int64 literal 137
int64Term :: Int64 -> TTerm Term
int64Term n = inject (Core.nameLift _Term) "literal" $
inject (Core.nameLift _Literal) "integer" $
inject (Core.nameLift _IntegerValue) "int64" $ int64 n
-- | Create a meta-level term encoding a boolean literal
-- Example: booleanTerm True creates a term that *represents* the boolean True
booleanTerm :: Bool -> TTerm Term
booleanTerm b = inject (Core.nameLift _Term) "literal" $ inject (Core.nameLift _Literal) "boolean" $ boolean b
-- | Create a meta-level term encoding a Name
-- Example: nameTerm "foo" creates a term that *represents* the name "foo"
nameTerm :: String -> TTerm Term
nameTerm s = wrap (Core.nameLift _Name) $ string s
-- | Term-encoded boolean true literal
true :: TTerm Term
true = boolean True
-- | Create a term-encoded tuple with multiple components using nested pairs
-- Example: tuple [string "name", int32 42, boolean True]
tuple :: [TTerm Term] -> TTerm Term
tuple [] = unit
tuple [a] = a
tuple [a, b] = pair a b
tuple (a:rest) = pair a (tuple rest)
-- | Create a term-encoded 2-tuple (same as pair)
-- Example: tuple2 (string "name") (int32 42)
tuple2 :: TTerm Term -> TTerm Term -> TTerm Term
tuple2 = pair
-- | Create a term-encoded 8-bit unsigned integer literal
-- Example: uint8 255
uint8 :: Int16 -> TTerm Term
uint8 = uint8Lift . TTerm . Terms.uint8
uint8Lift :: TTerm Int16 -> TTerm Term
uint8Lift = Core.termLiteral . Core.literalInteger . Core.integerValueUint8
-- | Create a term-encoded 16-bit unsigned integer literal
-- Example: uint16 65535
uint16 :: Int -> TTerm Term
uint16 = uint16Lift . TTerm . Terms.uint16
-- | Lift a TTerm Int to a term-encoded uint16 literal
-- Example: uint16Lift (varPhantom "x" :: TTerm Int)
uint16Lift :: TTerm Int -> TTerm Term
uint16Lift = Core.termLiteral . Core.literalInteger . Core.integerValueUint16
-- | Create a term-encoded 32-bit unsigned integer literal
-- Example: uint32 4294967295
uint32 :: Int64 -> TTerm Term
uint32 = uint32Lift . TTerm . Terms.uint32
-- | Lift a TTerm Int64 to a term-encoded uint32 literal
uint32Lift :: TTerm Int64 -> TTerm Term
uint32Lift = Core.termLiteral . Core.literalInteger . Core.integerValueUint32
-- | Create a term-encoded 64-bit unsigned integer literal
-- Example: uint64 18446744073709551615
uint64 :: Integer -> TTerm Term
uint64 = uint64Lift . TTerm . Terms.uint64
-- | Lift a TTerm Integer to a term-encoded uint64 literal
-- Example: uint64Lift (varPhantom "x" :: TTerm Integer)
uint64Lift :: TTerm Integer -> TTerm Term
uint64Lift = Core.termLiteral . Core.literalInteger . Core.integerValueUint64
-- | Create a term-encoded unit value
-- Example: unit
unit :: TTerm Term
unit = Core.termUnit
injectUnitPhantom :: Name -> Name -> TTerm Term
injectUnitPhantom tname fname = injectPhantom tname fname Core.termUnit
-- | Create a term-encoded unwrap function for a wrapped type
-- Example: unwrap (name "Email")
unwrap :: AsTerm t Name => t -> TTerm Term
unwrap = Core.termUnwrap . asTerm
-- | Create a term-encoded variable reference from a string
-- Example: var "x"
var :: String -> TTerm Term
var = Core.termVariable . name
-- | Create a term-encoded variable reference from a Name
-- Example: varName (Name "x")
varName :: Name -> TTerm Term
varName (Name n) = Core.termVariable $ TTerm $ Terms.string n
-- | Create a phantom-typed variable reference from a Name
-- Example: varNamePhantom (Name "x") :: TTerm Int
varNamePhantom :: Name -> TTerm a
varNamePhantom = TTerm . TermVariable
-- | Maps a string to a phantom-typed variable term
-- Example: varPhantom "x" :: TTerm Int
varPhantom :: String -> TTerm a
varPhantom = TTerm . TermVariable . Name
injectPhantom :: Name -> Name -> TTerm Term -> TTerm Term
injectPhantom tname fname term = Core.termInject $ Core.injection (Core.nameLift tname) $ Core.field (Core.nameLift fname) term
-- | Create a term-encoded wrapped term (newtype)
-- Example: wrap (name "Email") (string "user@example.com")
wrap :: AsTerm t Name => t -> TTerm Term -> TTerm Term
wrap n = Core.termWrap . Core.wrappedTerm (asTerm n)
-- | Create a term-encoded lambda with a type annotation
-- Example: lambdaTyped "x" T.int32 (var "x")
lambdaTyped :: String -> TTerm Type -> TTerm Term -> TTerm Term
lambdaTyped param dom body = Core.termLambda $ Core.lambda (name param) (Phantoms.just dom) body
-- | Create a term-encoded type application
-- Example: tyapp (list []) T.int32
tyapp :: TTerm Term -> TTerm Type -> TTerm Term
tyapp term typ = Core.termTypeApplication $ Core.typeApplicationTerm term typ
-- | Apply multiple type arguments to a term
-- Example: tyapps (pair (int32 1) (string "a")) [T.int32, T.string]
tyapps :: TTerm Term -> [TTerm Type] -> TTerm Term
tyapps = L.foldl tyapp
-- | Apply type arguments to a polymorphic term (same as tyapps)
-- Example: typeApplication (list []) [T.int32]
typeApplication :: TTerm Term -> [TTerm Type] -> TTerm Term
typeApplication = tyapps
-- | Create a term-encoded type lambda (System F)
-- Example: tylam "t0" (lambda "x" $ var "x")
tylam :: String -> TTerm Term -> TTerm Term
tylam var body = Core.termTypeLambda $ Core.typeLambda (name var) body
-- | Create multiple term-encoded type lambdas
-- Example: tylams ["t0", "t1"] (lambda "x" $ var "x")
tylams :: [String] -> TTerm Term -> TTerm Term
tylams vars body = L.foldl (\b v -> Core.termTypeLambda $ Core.typeLambda (name v) b) body $ L.reverse vars
-- | Create term-encoded type lambda(s) (same as tylams)
-- Example: typeLambda ["t0"] (lambda "x" $ var "x")
typeLambda :: [String] -> TTerm Term -> TTerm Term
typeLambda = tylams
-- | Create a term-encoded let expression with type annotations on bindings
-- Example: letsTyped [("x", int32 1, T.mono T.int32)] (var "x")
letsTyped :: [(String, TTerm Term, TTerm TypeScheme)] -> TTerm Term -> TTerm Term
letsTyped bindings body = Core.termLet $ Core.let_ (Phantoms.list $ toBinding bindings) body
where
toBinding = fmap (\(n, t, ts) -> Core.binding (name n) t (Phantoms.just ts))
-- | Create a term-encoded literal from a Literal value
-- Example: literal (LiteralInteger (IntegerValueInt32 42))
literal :: TTerm Literal -> TTerm Term
literal = Core.termLiteral
-- | Create a term-encoded character literal via int32
-- Example: char 'A'
char :: Char -> TTerm Term
char = int32 . ord
where
ord = fromEnum
-- | Term-encoded function composition (apply g then f)
-- Example: compose f g creates a function that applies g then f
compose :: TTerm Term -> TTerm Term -> TTerm Term
compose f g = lambda "arg_" $ f @@ (g @@ var "arg_")
-- | Term-encoded identity function
-- Example: identity
identity :: TTerm Term
identity = lambda "x_" $ var "x_"
-- | Create a term-encoded 3-tuple using nested pairs
-- Example: triple (int32 1) (string "test") (boolean True)
triple :: TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term
triple t1 t2 t3 = pair t1 (pair t2 t3)
-- | Create a term-encoded 4-tuple using nested pairs
-- Example: tuple4 (int32 1) (string "test") (boolean True) (float32 3.14)
tuple4 :: TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term
tuple4 t1 t2 t3 t4 = pair t1 (pair t2 (pair t3 t4))
-- | Create a term-encoded 5-tuple using nested pairs
-- Example: tuple5 (int32 1) (string "a") (boolean True) (float32 3.14) (int32 42)
tuple5 :: TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term -> TTerm Term
tuple5 t1 t2 t3 t4 t5 = pair t1 (pair t2 (pair t3 (pair t4 t5)))
-- | Convert a Comparison enum value to a term-encoded term
-- Example: comparison ComparisonEqualTo
comparison :: Comparison -> TTerm Term
comparison t = case t of
ComparisonEqualTo -> Phantoms.injectUnit _Comparison _Comparison_equalTo
ComparisonLessThan -> Phantoms.injectUnit _Comparison _Comparison_lessThan
ComparisonGreaterThan -> Phantoms.injectUnit _Comparison _Comparison_greaterThan