hydra-0.8.0: src/main/haskell/Hydra/Dsl/Base.hs
-- | Base DSL which makes use of phantom types. Use this DSL for defining programs as opposed to data type definitions.
module Hydra.Dsl.Base (
module Hydra.Dsl.Base,
module Hydra.Dsl.PhantomLiterals,
module Hydra.Dsl.ShorthandTypes,
hydraCore,
) where
import Hydra.Coders
import Hydra.Core
import Hydra.Compute
import Hydra.Graph
import Hydra.Annotations
import Hydra.Phantoms
import Hydra.Module
import qualified Hydra.Tier1 as Tier1
import Hydra.Dsl.PhantomLiterals
import Hydra.Dsl.ShorthandTypes
import Hydra.Sources.Core
import qualified Hydra.Dsl.Terms as Terms
import qualified Hydra.Dsl.Types as Types
import qualified Hydra.Dsl.Lib.Lists as Lists
import Hydra.Sources.Libraries
import Prelude hiding ((++))
import Data.String(IsString(..))
import qualified Data.Map as M
import qualified Data.Set as S
instance IsString (TTerm a) where fromString = TTerm . Terms.string
infixr 0 >:
(>:) :: String -> TTerm a -> Field
n >: d = Field (Name n) (unTTerm d)
infixr 0 >>:
(>>:) :: Name -> TTerm a -> Field
fname >>: d = Field fname (unTTerm d)
(<.>) :: TTerm (b -> c) -> TTerm (a -> b) -> TTerm (a -> c)
f <.> g = compose f g
-- Two alternative symbols for typed term application
(@@) :: TTerm (a -> b) -> TTerm a -> TTerm b
f @@ x = apply f x
($$) :: TTerm (a -> b) -> TTerm a -> TTerm b
f $$ x = apply f x
infixr 0 @->
(@->) :: a -> b -> (a, b)
x @-> y = (x, y)
infixr 0 -->
(-->) :: TCase a -> TTerm (a -> b) -> Field
c --> t = caseField c t
apply :: TTerm (a -> b) -> TTerm a -> TTerm b
apply (TTerm lhs) (TTerm rhs) = TTerm $ Terms.apply lhs rhs
apply2 :: TTerm (a -> b -> c) -> TTerm a -> TTerm b -> TTerm c
apply2 (TTerm f) (TTerm a1) (TTerm a2) = TTerm $ Terms.apply (Terms.apply f a1) a2
caseField :: TCase a -> TTerm (a -> b) -> Field
caseField (TCase fname) (TTerm f) = Field fname f
compose :: TTerm (b -> c) -> TTerm (a -> b) -> TTerm (a -> c)
compose (TTerm f) (TTerm g) = TTerm $ Terms.compose f g
constant :: TTerm a -> TTerm (b -> a)
constant (TTerm term) = TTerm $ Terms.constant term
definitionInModule :: Module -> String -> TTerm a -> TElement a
definitionInModule mod lname = TElement $ Tier1.unqualifyName $ QualifiedName (Just $ moduleNamespace mod) lname
doc :: String -> TTerm a -> TTerm a
doc s (TTerm term) = TTerm $ setTermDescription (Just s) term
doc70 :: String -> TTerm a -> TTerm a
doc70 = doc . wrapLine 70
doc80 :: String -> TTerm a -> TTerm a
doc80 = doc . wrapLine 80
el :: TElement a -> Element
el (TElement name (TTerm term)) = Element name term
field :: Name -> TTerm a -> Field
field fname (TTerm val) = Field fname val
first :: TTerm ((a, b) -> a)
first = TTerm $ Terms.untuple 2 0
fld :: Name -> TTerm a -> TField a
fld fname (TTerm val) = TField $ Field fname val
fold :: TTerm (b -> a -> b) -> TTerm (b -> [a] -> b)
fold f = Lists.foldl @@ f
function :: Type -> Type -> TTerm a -> TTerm a
function dom cod = typed (Types.function dom cod)
functionN :: [Type] -> TTerm a -> TTerm a
functionN ts = typed $ Types.functionN ts
functionNWithClasses :: [Type] -> M.Map Name (S.Set TypeClass) -> TTerm a -> TTerm a
functionNWithClasses ts classes = typed $ setTypeClasses classes (Types.functionN ts)
functionWithClasses :: Type -> Type -> M.Map Name (S.Set TypeClass) -> TTerm a -> TTerm a
functionWithClasses dom cod classes = typed $ setTypeClasses classes (Types.function dom cod)
-- Note: Haskell has trouble type-checking this construction if the convenience functions from Base are used
ifElse :: TTerm Bool -> TTerm a -> TTerm a -> TTerm a
ifElse (TTerm cond) (TTerm ifTrue) (TTerm ifFalse) = TTerm $
Terms.apply (Terms.apply (Terms.apply (Terms.primitive _logic_ifElse) ifTrue) ifFalse) cond
ifOpt :: TTerm (Maybe a) -> TTerm b -> TTerm (a -> b) -> TTerm b
ifOpt m n j = matchOpt n j @@ m
identity :: TTerm (a -> a)
identity = TTerm Terms.identity
inject :: Name -> Name -> TTerm a -> TTerm b
inject name fname (TTerm term) = TTerm $ Terms.inject name (Field fname term)
inject2 :: Name -> Name -> TTerm (a -> b)
inject2 name fname = lambda "x2" $ inject name fname $ var "x2"
just :: TTerm x -> TTerm (Maybe x)
just (TTerm term) = TTerm $ Terms.just term
lambda :: String -> TTerm x -> TTerm (a -> b)
lambda v (TTerm body) = TTerm $ Terms.lambda v body
--letTerm :: Var a -> TTerm a -> TTerm b -> TTerm b
--letTerm (Var k) (TTerm v) (TTerm env) = TTerm $ Terms.letTerm (Name k) v env
list :: [TTerm a] -> TTerm [a]
list els = TTerm $ Terms.list (unTTerm <$> els)
map :: M.Map (TTerm a) (TTerm b) -> TTerm (M.Map a b)
map = TTerm . Terms.map . M.fromList . fmap fromTTerm . M.toList
where
fromTTerm (TTerm k, TTerm v) = (k, v)
match :: Name -> Maybe (TTerm b) -> [Field] -> TTerm (u -> b)
match name dflt fields = TTerm $ Terms.match name (unTTerm <$> dflt) fields
matchData :: Name -> Maybe (TTerm b) -> [(Name, TTerm (x -> b))] -> TTerm (a -> b)
matchData name dflt pairs = TTerm $ Terms.match name (unTTerm <$> dflt) (toField <$> pairs)
where
toField (fname, TTerm term) = Field fname term
matchOpt :: TTerm b -> TTerm (a -> b) -> TTerm (Maybe a -> b)
matchOpt (TTerm n) (TTerm j) = TTerm $ Terms.matchOpt n j
matchToEnum :: Name -> Name -> Maybe (TTerm b) -> [(Name, Name)] -> TTerm (a -> b)
matchToEnum domName codName dflt pairs = matchData domName dflt (toCase <$> pairs)
where
toCase (fromName, toName) = (fromName, constant $ unitVariant codName toName)
matchToUnion :: Name -> Name -> Maybe (TTerm b) -> [(Name, Field)] -> TTerm (a -> b)
matchToUnion domName codName dflt pairs = matchData domName dflt (toCase <$> pairs)
where
toCase (fromName, fld) = (fromName, constant $ TTerm $ Terms.inject codName fld)
-- Note: the phantom types provide no guarantee of type safety in this case
nom :: Name -> TTerm a -> TTerm b
nom name (TTerm term) = TTerm $ Terms.wrap name term
nothing :: TTerm a
nothing = TTerm Terms.nothing
opt :: Maybe (TTerm a) -> TTerm (Maybe a)
opt mc = TTerm $ Terms.optional (unTTerm <$> mc)
pair :: (TTerm a) -> (TTerm b) -> TTerm (a, b)
pair (TTerm l) (TTerm r) = TTerm $ Terms.pair l r
primitive :: Name -> TTerm a
primitive = TTerm . Terms.primitive
project :: Name -> Name -> TTerm (a -> b)
project name fname = TTerm $ Terms.project name fname
record :: Name -> [Field] -> TTerm a
record name fields = TTerm $ Terms.record name fields
ref :: TElement a -> TTerm a
ref (TElement name _) = TTerm (TermVariable name)
second :: TTerm ((a, b) -> b)
second = TTerm $ Terms.untuple 2 1
set :: S.Set (TTerm a) -> TTerm (S.Set a)
set = TTerm . Terms.set . S.fromList . fmap unTTerm . S.toList
typed :: Type -> TTerm a -> TTerm a
typed typ (TTerm term) = TTerm $ setTermType (Just typ) term
unit :: TTerm a
unit = TTerm Terms.unit
unitVariant :: Name -> Name -> TTerm a
unitVariant name fname = TTerm $ Terms.inject name $ Field fname Terms.unit
unwrap :: Name -> TTerm (a -> b)
unwrap = TTerm . Terms.unwrap
var :: String -> TTerm a
var v = TTerm $ Terms.var v
variant :: Name -> Name -> TTerm a -> TTerm b
variant name fname (TTerm term) = TTerm $ Terms.inject name $ Field fname term
with :: TTerm a -> [Field] -> TTerm a
(TTerm env) `with` fields = TTerm $ TermLet $ Let (toBinding <$> fields) env
where
toBinding (Field name value) = LetBinding name value Nothing
wrap :: Name -> TTerm a -> TTerm b
wrap name (TTerm term) = TTerm $ Terms.wrap name term