egison-5.0.0: hs-src/Language/Egison/IExpr.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{- |
Module : Language.Egison.IExpr
Licence : MIT
This module defines internal representation of Egison language.
-}
module Language.Egison.IExpr
( ITopExpr (..)
, IExpr (..)
, IPattern (..)
, ILoopRange (..)
, IBindingExpr
, IMatchClause
, IPatternDef
, IPrimitiveDataPattern
-- Typed versions
, TITopExpr (..)
, TIExpr (..)
, TIExprNode (..)
, TIPattern (..)
, TIPatternNode (..)
, TILoopRange (..)
, TIBindingExpr
, TIMatchClause
, TIPatternDef
, tiExprType
, tiExprScheme
, tiExprTypeVars
, tiExprConstraints
, tipType
, stripType
, stripTypeTopExpr
, Var (..)
, stringToVar
, extractNameFromVar
, Index (..)
, extractSupOrSubIndex
, extractIndex
, makeIApply
-- Re-export from AST
, ConstantExpr (..)
, PMMode (..)
, PrimitivePatPattern (..)
, PDPatternBase (..)
) where
import Data.Hashable
import GHC.Generics (Generic)
import Language.Egison.AST (ConstantExpr (..), PDPatternBase (..), PMMode (..), PrimitivePatPattern (..))
import Language.Egison.Type.Types (Type(..), TypeScheme(..), Constraint(..), TyVar(..))
data ITopExpr
= IDefine Var IExpr
| IDefineMany [(Var, IExpr)] -- Multiple definitions (for type class instances)
| ITest IExpr
| IExecute IExpr
| ILoadFile String
| ILoad String
| IDeclareSymbol [String] (Maybe Type) -- Symbol declaration
| IPatternFunctionDecl String [TyVar] [(String, Type)] Type IPattern -- Pattern function declaration
-- String: function name
-- [TyVar]: type parameters
-- [(String, Type)]: parameters (name and type)
-- Type: return type
-- IPattern: body
deriving Show
data IExpr
= IConstantExpr ConstantExpr
| IVarExpr String
| IIndexedExpr Bool IExpr [Index IExpr]
| ISubrefsExpr Bool IExpr IExpr
| ISuprefsExpr Bool IExpr IExpr
| IUserrefsExpr Bool IExpr IExpr
| IInductiveDataExpr String [IExpr]
| ITupleExpr [IExpr]
| ICollectionExpr [IExpr]
| IConsExpr IExpr IExpr
| IJoinExpr IExpr IExpr
| IHashExpr [(IExpr, IExpr)]
| IVectorExpr [IExpr]
| ILambdaExpr (Maybe Var) [Var] IExpr
| IMemoizedLambdaExpr [String] IExpr
| ICambdaExpr String IExpr
| IIfExpr IExpr IExpr IExpr
| ILetRecExpr [IBindingExpr] IExpr
| ILetExpr [IBindingExpr] IExpr
| IWithSymbolsExpr [String] IExpr
| IMatchExpr PMMode IExpr IExpr [IMatchClause]
| IMatchAllExpr PMMode IExpr IExpr [IMatchClause]
| IMatcherExpr [IPatternDef]
| IQuoteExpr IExpr
| IQuoteSymbolExpr IExpr
| IWedgeApplyExpr IExpr [IExpr]
| IDoExpr [IBindingExpr] IExpr
| ISeqExpr IExpr IExpr
| IApplyExpr IExpr [IExpr]
| IGenerateTensorExpr IExpr IExpr
| ITensorExpr IExpr IExpr
| ITensorContractExpr IExpr
| ITensorMapExpr IExpr IExpr
| ITensorMap2Expr IExpr IExpr IExpr
| ITensorMap2WedgeExpr IExpr IExpr IExpr
| ITransposeExpr IExpr IExpr
| IFlipIndicesExpr IExpr
| IFunctionExpr [String]
| IPatternFuncExpr [String] IPattern -- Pattern function: parameter names and pattern body
deriving Show
type IBindingExpr = (IPrimitiveDataPattern, IExpr)
type IMatchClause = (IPattern, IExpr)
type IPatternDef = (PrimitivePatPattern, IExpr, [(IPrimitiveDataPattern, IExpr)])
type IPrimitiveDataPattern = PDPatternBase Var
data IPattern
= IWildCard
| IPatVar String
| IValuePat IExpr
| IPredPat IExpr
| IIndexedPat IPattern [IExpr]
| ILetPat [IBindingExpr] IPattern
| INotPat IPattern
| IAndPat IPattern IPattern
| IOrPat IPattern IPattern
| IForallPat IPattern IPattern
| ITuplePat [IPattern]
| IInductivePat String [IPattern]
| ILoopPat String ILoopRange IPattern IPattern
| IContPat
| IPApplyPat IExpr [IPattern]
| IVarPat String
| IInductiveOrPApplyPat String [IPattern]
| ISeqNilPat
| ISeqConsPat IPattern IPattern
| ILaterPatVar
-- For symbolic computing
| IDApplyPat IPattern [IPattern]
deriving Show
data ILoopRange = ILoopRange IExpr IExpr IPattern
deriving Show
data Index a
= Sub a
| Sup a
| MultiSub a Integer a
| MultiSup a Integer a
| SupSub a
| User a
| DF Integer Integer
deriving (Show, Eq, Ord, Functor, Foldable, Generic, Traversable)
extractSupOrSubIndex :: Index a -> Maybe a
extractSupOrSubIndex (Sub x) = Just x
extractSupOrSubIndex (Sup x) = Just x
extractSupOrSubIndex (SupSub x) = Just x
extractSupOrSubIndex _ = Nothing
extractIndex :: Index a -> a
extractIndex (Sub x) = x
extractIndex (Sup x) = x
extractIndex (SupSub x) = x
extractIndex (User x) = x
extractIndex DF{} = undefined
data Var = Var String [Index (Maybe Var)]
deriving (Generic, Show)
-- for eq, ord and hashable
data Var' = Var' String [Index ()]
deriving (Eq, Ord, Generic, Show)
instance Eq Var where
Var name (MultiSup _ _ _:_) == Var name' is' = Var name [] == Var name' is'
Var name (MultiSub _ _ _:_) == Var name' is' = Var name [] == Var name' is'
Var name is == Var name' (MultiSup _ _ _:_) = Var name is == Var name' []
Var name is == Var name' (MultiSub _ _ _:_) = Var name is == Var name' []
Var name is == Var name' is' = Var' name (map (fmap (\_ -> ())) is) == Var' name' (map (fmap (\_ -> ())) is')
instance Ord Var where
compare (Var name (MultiSup _ _ _:_)) (Var name' is') = compare (Var name []) (Var name' is')
compare (Var name (MultiSub _ _ _:_)) (Var name' is') = compare (Var name []) (Var name' is')
compare (Var name is) (Var name' (MultiSup _ _ _:_)) = compare (Var name is) (Var name' [])
compare (Var name is) (Var name' (MultiSub _ _ _:_)) = compare (Var name is) (Var name' [])
compare (Var name is) (Var name' is') =
compare (Var' name (map (fmap (\_ -> ())) is)) (Var' name' (map (fmap (\_ -> ())) is'))
instance Hashable a => Hashable (Index a)
instance Hashable Var'
instance Hashable Var where
hashWithSalt salt (Var name (MultiSup _ _ _:_)) = hashWithSalt salt (Var' name [])
hashWithSalt salt (Var name (MultiSub _ _ _:_)) = hashWithSalt salt (Var' name [])
hashWithSalt salt (Var name is) = hashWithSalt salt (Var' name (map (fmap (\_ -> ())) is))
stringToVar :: String -> Var
stringToVar name = Var name []
extractNameFromVar :: Var -> String
extractNameFromVar (Var name _) = name
makeIApply :: String -> [IExpr] -> IExpr
makeIApply fn args = IApplyExpr (IVarExpr fn) args
--
-- Typed Internal Expressions
--------------------------------------------------------------------------------
-- Phase 9: TIExpr - Evaluatable Typed IR with Type Info Preserved
--------------------------------------------------------------------------------
-- TIExpr is the result of Phase 8 (TypedDesugar) and input to Phase 10 (Evaluation).
-- It carries type information alongside the expression for:
-- - Better runtime error messages with type information
-- - Type-based dispatch during evaluation
-- - Debugging support with type annotations
--
-- Design Decision (design/implementation.md):
-- Type information is preserved after TypedDesugar for better error messages.
-- Type classes have already been resolved to dictionary passing, so no type class
-- constraints are needed here.
-- | Typed top-level expression (Phase 9: TITopExpr)
-- Result of TypedDesugar phase, ready for evaluation.
data TITopExpr
= TIDefine TypeScheme Var TIExpr -- ^ Typed definition with type scheme (includes type vars & constraints)
| TIDefineMany [(Var, TIExpr)] -- ^ Multiple definitions (letrec)
| TITest TIExpr -- ^ Test expression (REPL)
| TIExecute TIExpr -- ^ Execute IO expression
| TILoadFile String -- ^ Load file (should not appear after expandLoads)
| TILoad String -- ^ Load library (should not appear after expandLoads)
| TIDeclareSymbol [String] Type -- ^ Typed symbol declaration
| TIPatternFunctionDecl String TypeScheme [(String, Type)] Type TIPattern -- ^ Typed pattern function declaration
-- String: function name
-- TypeScheme: type scheme with type parameters and constraints
-- [(String, Type)]: parameters (name and type with type params substituted)
-- Type: return type (with type params substituted)
-- TIPattern: typed body
deriving Show
-- | Typed internal expression (Phase 9: TIExpr)
-- Each expression node carries its inferred/checked type scheme with type variables and constraints.
-- TypeScheme info is preserved for Phase 8 (TypedDesugar) to perform type-driven transformations
-- such as type class dictionary passing and tensorMap insertion.
--
-- NEW: TIExpr is now RECURSIVE - each sub-expression is also a TIExpr,
-- allowing type information to be preserved throughout the tree.
-- This eliminates the need to re-run type inference during TypeClassExpand.
data TIExpr = TIExpr
{ tiScheme :: TypeScheme -- ^ Type scheme with type variables, constraints, and type
, tiExprNode :: TIExprNode -- ^ Typed expression node with typed sub-expressions
} deriving Show
-- | Typed expression node - each constructor contains typed sub-expressions (TIExpr)
-- This mirrors IExpr but with TIExpr in place of IExpr for all sub-expressions
data TIExprNode
-- Constants and variables
= TIConstantExpr ConstantExpr
| TIVarExpr String
-- Collections
| TITupleExpr [TIExpr]
| TICollectionExpr [TIExpr]
| TIConsExpr TIExpr TIExpr
| TIJoinExpr TIExpr TIExpr
| TIHashExpr [(TIExpr, TIExpr)]
| TIVectorExpr [TIExpr]
-- Lambda expressions
| TILambdaExpr (Maybe Var) [Var] TIExpr
| TIMemoizedLambdaExpr [String] TIExpr
| TICambdaExpr String TIExpr
-- Application
| TIApplyExpr TIExpr [TIExpr]
-- Control flow
| TIIfExpr TIExpr TIExpr TIExpr
-- Let expressions
| TILetExpr [TIBindingExpr] TIExpr
| TILetRecExpr [TIBindingExpr] TIExpr
| TIWithSymbolsExpr [String] TIExpr
-- Pattern matching
| TIMatchExpr PMMode TIExpr TIExpr [TIMatchClause]
| TIMatchAllExpr PMMode TIExpr TIExpr [TIMatchClause]
| TIMatcherExpr [TIPatternDef]
-- Inductive data
| TIInductiveDataExpr String [TIExpr]
-- Quote expressions
| TIQuoteExpr TIExpr
| TIQuoteSymbolExpr TIExpr
-- Indexed expressions
| TIIndexedExpr Bool TIExpr [Index TIExpr]
| TISubrefsExpr Bool TIExpr TIExpr
| TISuprefsExpr Bool TIExpr TIExpr
| TIUserrefsExpr Bool TIExpr TIExpr
-- Application variants
| TIWedgeApplyExpr TIExpr [TIExpr]
-- Do expressions
| TIDoExpr [TIBindingExpr] TIExpr
-- Sequence
| TISeqExpr TIExpr TIExpr
-- Tensor operations
| TIGenerateTensorExpr TIExpr TIExpr
| TITensorExpr TIExpr TIExpr
| TITensorContractExpr TIExpr
| TITensorMapExpr TIExpr TIExpr
| TITensorMap2Expr TIExpr TIExpr TIExpr
| TITensorMap2WedgeExpr TIExpr TIExpr TIExpr -- Like TensorMap2 but supplements different indices
| TITransposeExpr TIExpr TIExpr
| TIFlipIndicesExpr TIExpr
-- Function reference
| TIFunctionExpr [String]
deriving Show
-- | Typed binding expression
type TIBindingExpr = (IPrimitiveDataPattern, TIExpr)
-- | Typed match clause
type TIMatchClause = (TIPattern, TIExpr)
-- | Typed pattern definition (for matcher expressions)
type TIPatternDef = (PrimitivePatPattern, TIExpr, [TIBindingExpr])
-- | Get the type of a typed expression (extracts Type from TypeScheme)
tiExprType :: TIExpr -> Type
tiExprType (TIExpr (Forall _ _ t) _) = t
-- | Get the type scheme of a typed expression
tiExprScheme :: TIExpr -> TypeScheme
tiExprScheme = tiScheme
-- | Get the type variables of a typed expression
tiExprTypeVars :: TIExpr -> [TyVar]
tiExprTypeVars (TIExpr (Forall tvs _ _) _) = tvs
-- | Get the constraints of a typed expression
tiExprConstraints :: TIExpr -> [Constraint]
tiExprConstraints (TIExpr (Forall _ cs _) _) = cs
-- | Strip type information, returning the untyped expression
-- This recursively converts TIExpr back to IExpr for evaluation
stripType :: TIExpr -> IExpr
stripType (TIExpr _ node) = case node of
TIConstantExpr c -> IConstantExpr c
TIVarExpr name -> IVarExpr name
TITupleExpr exprs -> ITupleExpr (map stripType exprs)
TICollectionExpr exprs -> ICollectionExpr (map stripType exprs)
TIConsExpr e1 e2 -> IConsExpr (stripType e1) (stripType e2)
TIJoinExpr e1 e2 -> IJoinExpr (stripType e1) (stripType e2)
TIHashExpr pairs -> IHashExpr [(stripType k, stripType v) | (k, v) <- pairs]
TIVectorExpr exprs -> IVectorExpr (map stripType exprs)
TILambdaExpr mVar params body -> ILambdaExpr mVar params (stripType body)
TIMemoizedLambdaExpr args body -> IMemoizedLambdaExpr args (stripType body)
TICambdaExpr var body -> ICambdaExpr var (stripType body)
TIApplyExpr func args -> IApplyExpr (stripType func) (map stripType args)
TIIfExpr cond thenE elseE -> IIfExpr (stripType cond) (stripType thenE) (stripType elseE)
TILetExpr bindings body -> ILetExpr (map stripTypeBinding bindings) (stripType body)
TILetRecExpr bindings body -> ILetRecExpr (map stripTypeBinding bindings) (stripType body)
TIWithSymbolsExpr syms body -> IWithSymbolsExpr syms (stripType body)
TIMatchExpr mode target matcher clauses ->
IMatchExpr mode (stripType target) (stripType matcher) (map stripTypeClause clauses)
TIMatchAllExpr mode target matcher clauses ->
IMatchAllExpr mode (stripType target) (stripType matcher) (map stripTypeClause clauses)
TIMatcherExpr patDefs ->
IMatcherExpr [(pat, stripType expr, map stripTypeBinding bindings) | (pat, expr, bindings) <- patDefs]
TIInductiveDataExpr name exprs -> IInductiveDataExpr name (map stripType exprs)
TIQuoteExpr e -> IQuoteExpr (stripType e)
TIQuoteSymbolExpr e -> IQuoteSymbolExpr (stripType e)
TIIndexedExpr override expr indices -> IIndexedExpr override (stripType expr) (fmap stripType <$> indices)
TISubrefsExpr b e1 e2 -> ISubrefsExpr b (stripType e1) (stripType e2)
TISuprefsExpr b e1 e2 -> ISuprefsExpr b (stripType e1) (stripType e2)
TIUserrefsExpr b e1 e2 -> IUserrefsExpr b (stripType e1) (stripType e2)
TIWedgeApplyExpr func args -> IWedgeApplyExpr (stripType func) (map stripType args)
TIDoExpr bindings body -> IDoExpr (map stripTypeBinding bindings) (stripType body)
TISeqExpr e1 e2 -> ISeqExpr (stripType e1) (stripType e2)
TIGenerateTensorExpr func shape -> IGenerateTensorExpr (stripType func) (stripType shape)
TITensorExpr shape elems -> ITensorExpr (stripType shape) (stripType elems)
TITensorContractExpr e -> ITensorContractExpr (stripType e)
TITensorMapExpr func tensor -> ITensorMapExpr (stripType func) (stripType tensor)
TITensorMap2Expr func t1 t2 -> ITensorMap2Expr (stripType func) (stripType t1) (stripType t2)
TITensorMap2WedgeExpr func t1 t2 -> ITensorMap2WedgeExpr (stripType func) (stripType t1) (stripType t2)
TITransposeExpr perm tensor -> ITransposeExpr (stripType perm) (stripType tensor)
TIFlipIndicesExpr tensor -> IFlipIndicesExpr (stripType tensor)
TIFunctionExpr names -> IFunctionExpr names
where
stripTypeBinding :: TIBindingExpr -> IBindingExpr
stripTypeBinding (pat, expr) = (pat, stripType expr)
stripTypeClause :: TIMatchClause -> IMatchClause
stripTypeClause (tipat, expr) = (stripTypePat tipat, stripType expr)
stripTypePat :: TIPattern -> IPattern
stripTypePat (TIPattern _ node) = case node of
TIWildCard -> IWildCard
TIPatVar name -> IPatVar name
TIValuePat expr -> IValuePat (stripType expr)
TIPredPat expr -> IPredPat (stripType expr)
TIIndexedPat pat exprs -> IIndexedPat (stripTypePat pat) (map stripType exprs)
TILetPat bindings pat -> ILetPat (map stripTypeBinding bindings) (stripTypePat pat)
TINotPat pat -> INotPat (stripTypePat pat)
TIAndPat p1 p2 -> IAndPat (stripTypePat p1) (stripTypePat p2)
TIOrPat p1 p2 -> IOrPat (stripTypePat p1) (stripTypePat p2)
TIForallPat p1 p2 -> IForallPat (stripTypePat p1) (stripTypePat p2)
TITuplePat pats -> ITuplePat (map stripTypePat pats)
TIInductivePat name pats -> IInductivePat name (map stripTypePat pats)
TILoopPat var range p1 p2 -> ILoopPat var (stripTypeLoopRange range) (stripTypePat p1) (stripTypePat p2)
TIContPat -> IContPat
TIPApplyPat func pats -> IPApplyPat (stripType func) (map stripTypePat pats)
TIVarPat name -> IVarPat name
TIInductiveOrPApplyPat name pats -> IInductiveOrPApplyPat name (map stripTypePat pats)
TISeqNilPat -> ISeqNilPat
TISeqConsPat p1 p2 -> ISeqConsPat (stripTypePat p1) (stripTypePat p2)
TILaterPatVar -> ILaterPatVar
TIDApplyPat pat pats -> IDApplyPat (stripTypePat pat) (map stripTypePat pats)
stripTypeLoopRange :: TILoopRange -> ILoopRange
stripTypeLoopRange (TILoopRange e1 e2 pat) = ILoopRange (stripType e1) (stripType e2) (stripTypePat pat)
_stripTypeIndex :: Index TIExpr -> Index IExpr
_stripTypeIndex idx = case idx of
DF i1 i2 -> DF i1 i2
Sub e -> Sub (stripType e)
Sup e -> Sup (stripType e)
MultiSub e1 n e2 -> MultiSub (stripType e1) n (stripType e2)
MultiSup e1 n e2 -> MultiSup (stripType e1) n (stripType e2)
SupSub e -> SupSub (stripType e)
User e -> User (stripType e)
-- | Strip type information from top-level expression
stripTypeTopExpr :: TITopExpr -> ITopExpr
stripTypeTopExpr (TIDefine _scheme var expr) = IDefine var (stripType expr)
stripTypeTopExpr (TIDefineMany bindings) = IDefineMany [(v, stripType e) | (v, e) <- bindings]
stripTypeTopExpr (TITest expr) = ITest (stripType expr)
stripTypeTopExpr (TIExecute expr) = IExecute (stripType expr)
stripTypeTopExpr (TILoadFile file) = ILoadFile file
stripTypeTopExpr (TILoad file) = ILoad file
stripTypeTopExpr (TIDeclareSymbol names ty) = IDeclareSymbol names (Just ty)
stripTypeTopExpr (TIPatternFunctionDecl name _scheme params retType body) =
IPatternFunctionDecl name tyVars params retType (stripTypePat body)
where
-- Extract type variables from the type scheme
Forall tyVars _ _ = _scheme
-- Helper function to strip type from pattern
stripTypePat :: TIPattern -> IPattern
stripTypePat (TIPattern _ node) = case node of
TIWildCard -> IWildCard
TIPatVar v -> IPatVar v
TIValuePat e -> IValuePat (stripType e)
TIPredPat e -> IPredPat (stripType e)
TIIndexedPat p es -> IIndexedPat (stripTypePat p) (map stripType es)
TILetPat binds p -> ILetPat [(pd, stripType e) | (pd, e) <- binds] (stripTypePat p)
TIAndPat p1 p2 -> IAndPat (stripTypePat p1) (stripTypePat p2)
TIOrPat p1 p2 -> IOrPat (stripTypePat p1) (stripTypePat p2)
TINotPat p -> INotPat (stripTypePat p)
TITuplePat ps -> ITuplePat (map stripTypePat ps)
TIInductivePat name ps -> IInductivePat name (map stripTypePat ps)
TIPApplyPat e ps -> IPApplyPat (stripType e) (map stripTypePat ps)
TIDApplyPat p ps -> IDApplyPat (stripTypePat p) (map stripTypePat ps)
TILoopPat v r p1 p2 -> ILoopPat v (stripTypeLoopRange r) (stripTypePat p1) (stripTypePat p2)
TIVarPat v -> IVarPat v
TIForallPat p1 p2 -> IForallPat (stripTypePat p1) (stripTypePat p2)
TIContPat -> IContPat
TISeqNilPat -> ISeqNilPat
TISeqConsPat p1 p2 -> ISeqConsPat (stripTypePat p1) (stripTypePat p2)
TILaterPatVar -> ILaterPatVar
TIInductiveOrPApplyPat name ps -> IInductiveOrPApplyPat name (map stripTypePat ps)
stripTypeLoopRange :: TILoopRange -> ILoopRange
stripTypeLoopRange (TILoopRange e1 e2 pat) = ILoopRange (stripType e1) (stripType e2) (stripTypePat pat)
-- | Typed pattern with recursive structure (like TIExpr)
data TIPattern = TIPattern
{ tipScheme :: TypeScheme -- ^ Type scheme with type variables and constraints
, tipPatternNode :: TIPatternNode -- ^ The pattern node
} deriving Show
-- | Pattern node with type information (recursive structure)
data TIPatternNode
= TIWildCard
| TIPatVar String
| TIValuePat TIExpr
| TIPredPat TIExpr
| TIIndexedPat TIPattern [TIExpr]
| TILetPat [TIBindingExpr] TIPattern
| TINotPat TIPattern
| TIAndPat TIPattern TIPattern
| TIOrPat TIPattern TIPattern
| TIForallPat TIPattern TIPattern
| TITuplePat [TIPattern]
| TIInductivePat String [TIPattern]
| TILoopPat String TILoopRange TIPattern TIPattern
| TIContPat
| TIPApplyPat TIExpr [TIPattern]
| TIVarPat String
| TIInductiveOrPApplyPat String [TIPattern]
| TISeqNilPat
| TISeqConsPat TIPattern TIPattern
| TILaterPatVar
| TIDApplyPat TIPattern [TIPattern]
deriving Show
-- | Get the type of a typed pattern (extracts Type from TypeScheme)
tipType :: TIPattern -> Type
tipType (TIPattern (Forall _ _ t) _) = t
-- | Typed loop range
data TILoopRange = TILoopRange TIExpr TIExpr TIPattern
deriving Show
-- NOTE: TIBindingExpr, TIMatchClause, and TIPatternDef are now defined
-- near TIExprNode (around line 302-308) to keep type definitions close together
instance {-# OVERLAPPING #-} Show (Index String) where
show (Sup s) = "~" ++ s
show (Sub s) = "_" ++ s
show (SupSub s) = "~_" ++ s
show (User s) = "|" ++ s
show (DF _ _) = ""