packages feed

hydra-0.8.0: src/main/haskell/Hydra/Inference/AltInference.hs

module Hydra.Inference.AltInference where

import Hydra.Basics
import Hydra.Core
import Hydra.Compute
import Hydra.Mantle
import qualified Hydra.Flows as F
import qualified Hydra.Tier1 as Tier1
import qualified Hydra.Lib.Flows as Flows
import qualified Hydra.Dsl.Types as Types

import qualified Data.List as L
import qualified Data.Map as M


--------------------------------------------------------------------------------
-- Graphs

showType :: Type -> String
showType (TypeFunction (FunctionType dom cod)) = "(" ++ showType dom ++ "→" ++ showType cod ++ ")"
showType (TypeList etyp) = "[" ++ showType etyp ++ "]"
showType (TypeLiteral lit) = show lit
showType (TypeMap (MapType keyTyp valTyp)) = "map<" ++ showType keyTyp ++ "," ++ showType valTyp ++ ">"
showType (TypeProduct types) = "(" ++ (L.intercalate "," (fmap showType types)) ++ ")"
showType (TypeVariable (Name name)) = name

showTypeScheme :: TypeScheme -> String
showTypeScheme (TypeScheme vars body) = "∀[" ++ (L.intercalate "," (fmap (\(Name name) -> name) vars)) ++ "]." ++ showType body

showConstraint :: TypeConstraint -> String
showConstraint (TypeConstraint ltyp rtyp _) = showType ltyp ++ "≡" ++ showType rtyp

--------------------------------------------------------------------------------
-- Unification

type UnificationContext = Maybe String

data SUnificationError
  = SUnificationErrorCannotUnify Type Type UnificationContext
  | SUnificationErrorOccursCheckFailed Name Type UnificationContext
  deriving (Eq, Ord, Show)

sOccursIn :: Name -> Type -> Bool
sOccursIn var typ = case typ of
  TypeFunction (FunctionType domTyp codTyp) -> sOccursIn var domTyp || sOccursIn var codTyp
  TypeList etyp -> sOccursIn var etyp
  TypeLiteral _ -> False
  TypeMap (MapType keyTyp valTyp) -> sOccursIn var keyTyp || sOccursIn var valTyp
  TypeProduct types -> any (sOccursIn var) types
  TypeVariable name -> var == name

-- sComposeSubst :: SSubst -> SSubst -> SSubst
-- sComposeSubst s1 s2 = ...

{-
Robinson's algorithm, following https://www.cs.cornell.edu/courses/cs6110/2017sp/lectures/lec23.pdf
Specifically this is an implementation of the following rules:
 * Unify({(x, t)} ∪ E) = {t/x} Unify(E{t/x}) if x ∉ FV(t)
 * Unify(∅) = I (the identity substitution x ↦ x)
 * Unify({(x, x)} ∪ E) = Unify(E)
 * Unify({(f(s1, ..., sn), f(t1, ..., tn))} ∪ E) = Unify({(s1, t1), ..., (sn, tn)} ∪ E)
-}
uUnify :: [TypeConstraint] -> Either SUnificationError SSubst
-- uUnify = L.foldl helper sEmptySubst
--   where
--     helper s (TypeConstraint t1 t2 ctx) = case t1 of
--       TypeVariable v1 -> case t2 of
--         TypeVariable v2 -> if v1 == v2
--           then Right s
--           else uBind v1 t2
--         _ -> unifyVar s v1 t2
--       _ -> case t2 of
--         TypeVariable v2 -> unifyVar v2 t1
--         _ -> unifyOther s t1 t2
--     unifyVar s v t = if sOccursIn v t -- TODO: expensive occurs check
--       then Left $ SUnificationErrorOccursCheckFailed v t ctx
--       else Right $ M.singleton v t
--     unifyOther s t1 t2 = ...
uUnify constraints = case constraints of
    [] -> Right sEmptySubst
    ((TypeConstraint t1 t2 ctx):rest) -> case t1 of
        TypeVariable v1 -> case t2 of
          TypeVariable v2 -> if v1 == v2
            then uUnify rest
            else uBind v1 t2
          _ -> uBind v1 t2
        _ -> case t2 of
          TypeVariable v2 -> uBind v2 t1
          _ -> uUnifyOther t1 t2
      where
        -- TODO: this occurs check is expensive; consider delaying it until the time of substitution
        uBind v t = if sOccursIn v t
            then Left $ SUnificationErrorOccursCheckFailed v t ctx
            else case uUnify (L.map (uSubstInConstraint v t) rest) of
              Left err -> Left err
              Right subst -> Right $ SSubst $ M.union (M.singleton v $ sSubstituteTypeVariables subst t) $ sUnSubst subst
        uUnifyOther t1 t2 = case t1 of
            TypeFunction (FunctionType dom1 cod1) -> case t2 of
              TypeFunction (FunctionType dom2 cod2) -> uUnify $ [
                (TypeConstraint dom1 dom2 ctx), (TypeConstraint cod1 cod2 ctx)] ++ rest
              _ -> cannotUnify
            TypeList l1 -> case t2 of
              TypeList l2 -> uUnify $ [(TypeConstraint l1 l2 ctx)] ++ rest
              _ -> cannotUnify
            TypeLiteral lit1 -> case t2 of
              TypeLiteral lit2 -> if lit1 == lit2
                then uUnify rest
                else cannotUnify
              _ -> cannotUnify
            TypeMap (MapType key1 val1) -> case t2 of
              TypeMap (MapType key2 val2) -> uUnify $ [
                (TypeConstraint key1 key2 ctx), (TypeConstraint val1 val2 ctx)] ++ rest
              _ -> cannotUnify
            TypeProduct types1 -> case t2 of
              TypeProduct types2 -> if L.length types1 /= L.length types2
                then cannotUnify
                else uUnify $ L.zipWith (\t1 t2 -> TypeConstraint t1 t2 ctx) types1 types2 ++ rest
              _ -> cannotUnify
          where
            cannotUnify = Left $ SUnificationErrorCannotUnify t1 t2 ctx

-- TODO: substituting one variable at a time is inefficient
uSubst :: Name -> Type -> Type -> Type
uSubst v t typ = case typ of
  TypeFunction (FunctionType dom cod) -> TypeFunction $ FunctionType (uSubst v t dom) (uSubst v t cod)
  TypeList etyp -> TypeList $ uSubst v t etyp
  TypeLiteral _ -> typ
  TypeMap (MapType key val) -> TypeMap $ MapType (uSubst v t key) (uSubst v t val)
  TypeProduct types -> TypeProduct $ fmap (uSubst v t) types
  TypeVariable name -> if name == v then t else typ

uSubstInConstraint :: Name -> Type -> TypeConstraint -> TypeConstraint
uSubstInConstraint v t (TypeConstraint t1 t2 ctx) = TypeConstraint (uSubst v t t1) (uSubst v t t2) ctx

--------------------------------------------------------------------------------
-- Substitution

data SSubst = SSubst { sUnSubst :: M.Map Name Type }

instance Show SSubst where
  show (SSubst subst) = "{" ++ L.intercalate ", " (fmap (\((Name k), v) -> k ++ ": " ++ showType v) $ M.toList subst) ++ "}"

sEmptySubst = SSubst M.empty

sSubstituteTypeVariables :: SSubst -> Type -> Type
sSubstituteTypeVariables subst typ = case typ of
  TypeFunction (FunctionType dom cod) -> TypeFunction $
    FunctionType (sSubstituteTypeVariables subst dom) (sSubstituteTypeVariables subst cod)
  TypeList etyp -> TypeList $ sSubstituteTypeVariables subst etyp
  TypeLiteral _ -> typ
  TypeMap (MapType key val) -> TypeMap $
    MapType (sSubstituteTypeVariables subst key) (sSubstituteTypeVariables subst val)
  TypeProduct types -> TypeProduct $ fmap (sSubstituteTypeVariables subst) types
  TypeVariable name -> case M.lookup name (sUnSubst subst) of
    Just styp -> styp
    Nothing -> typ

-- TODO: remove unused bound type variables
sSubstituteTypeVariablesInScheme :: SSubst -> TypeScheme -> TypeScheme
sSubstituteTypeVariablesInScheme subst (TypeScheme vars typ) = TypeScheme vars $ sSubstituteTypeVariables subst typ


--------------------------------------------------------------------------------
-- Inference

data SInferenceContext
  = SInferenceContext {
    sInferenceContextLexicon :: M.Map Name TypeScheme,
    sInferenceContextVariableCount :: Int,
    sInferenceContextTypingEnvironment :: M.Map Name TypeScheme}
  deriving (Eq, Ord, Show)

data SInferenceResult
  = SInferenceResult {
    sInferenceResultScheme :: TypeScheme,
    sInferenceResultConstraints :: [TypeConstraint]}
  deriving (Eq, Ord)
instance Show SInferenceResult where
  show (SInferenceResult scheme constraints) = "{type= " ++ showTypeScheme scheme ++ ", constraints= " ++ show constraints ++ "}"

sInferType :: Term -> Flow SInferenceContext TypeScheme
sInferType term = Flows.bind (sInferTypeInternal term) unifyAndSubst
  where
    unifyAndSubst :: SInferenceResult -> Flow SInferenceContext TypeScheme
    unifyAndSubst result = Flows.bind (F.fromEither $ uUnify $ sInferenceResultConstraints result) doSubst
      where
        doSubst :: SSubst -> Flow SInferenceContext TypeScheme
        doSubst subst = sInstantiateAndNormalize $ sSubstituteTypeVariablesInScheme subst $ sInferenceResultScheme result

sInferTypeInternal :: Term -> Flow SInferenceContext SInferenceResult
sInferTypeInternal term = case term of

    TermApplication (Application lterm rterm) -> Flows.bind sNewVar withVar1
      where
        withVar1 dom = Flows.bind sNewVar withVar2
          where
            withVar2 cod = Flows.bind (sInferTypeInternal lterm) withLeft
              where
                withLeft lresult = Flows.bind (sInferTypeInternal rterm) withRight
                  where
                    withRight rresult = Flows.pure $ SInferenceResult (TypeScheme tvars $ TypeVariable cod) $ [
                        TypeConstraint (Types.function (TypeVariable dom) (TypeVariable cod)) ltyp ctx,
                        TypeConstraint (TypeVariable dom) rtyp ctx]
                        ++ sInferenceResultConstraints lresult ++ sInferenceResultConstraints rresult
                      where
                        ctx = Just "application"
                        ltyp = typeSchemeType $ sInferenceResultScheme lresult
                        rtyp = typeSchemeType $ sInferenceResultScheme rresult
                        tvars = typeSchemeVariables (sInferenceResultScheme lresult) ++ typeSchemeVariables (sInferenceResultScheme rresult)

    TermFunction (FunctionLambda (Lambda var _ body)) -> Flows.bind sNewVar withVar
     where
        withVar tvar = sWithTypeBinding var (Types.mono $ TypeVariable tvar) $ Flows.map withBodyType (sInferTypeInternal body)
          where
            -- TODO: prove that tvar will never appear in vars
            withBodyType (SInferenceResult (TypeScheme vars t) constraints)
              = SInferenceResult (TypeScheme (tvar:vars) $ Types.function (TypeVariable tvar) t) constraints

    -- TODO: propagate rawValueVars and envVars into the final result, possibly after substitution
    -- TODO: recursive and mutually recursive let
    TermLet (Let bindings env) -> if L.length bindings > 2
        then sInferTypeInternal $ TermLet (Let [L.head bindings] $ TermLet $ Let (L.tail bindings) env)
        else forSingleBinding $ L.head bindings
      where
        forSingleBinding (LetBinding key value _) = Flows.bind sNewVar withVar
          where
            -- Create a temporary type variable for the binding
            withVar var = sWithTypeBinding key (Types.mono $ TypeVariable var) $
                Flows.bind (sInferTypeInternal value) withValueType
              where
                -- Unify and substitute over the value constraints
                -- TODO: save the substitution and pass it along, instead of the original set of constraints
                withValueType (SInferenceResult rawValueScheme valueConstraints) = Flows.bind (F.fromEither $ uUnify kvConstraints) afterUnification
                  where
                    rawValueVars = typeSchemeVariables rawValueScheme
                    kvConstraints = keyConstraint:valueConstraints
                    keyConstraint = TypeConstraint (TypeVariable var) (typeSchemeType rawValueScheme) $ Just "let binding"
                    -- Now update the type binding to use the inferred type
                    afterUnification subst = sWithTypeBinding key valueScheme
                        $ Flows.map withEnvType (sInferTypeInternal env)
                      where
                        valueScheme = sSubstituteTypeVariablesInScheme subst rawValueScheme
                        withEnvType (SInferenceResult envScheme envConstraints) = SInferenceResult envScheme constraints
                          where
                            constraints = kvConstraints ++ envConstraints
                            envVars = typeSchemeVariables envScheme

    TermList els -> Flows.bind sNewVar withVar
      where
        withVar tvar = if L.null els
            then Flows.pure $ yield (TypeScheme [tvar] $ Types.list $ TypeVariable tvar) []
            else Flows.map fromResults (Flows.sequence (sInferTypeInternal <$> els))
          where
            fromResults results = yield (TypeScheme vars $ Types.list $ TypeVariable tvar) constraints
              where
                uctx = Just "list element"
                constraints = cinner ++ couter
                cinner = L.concat (sInferenceResultConstraints <$> results)
                couter = fmap (\t -> TypeConstraint (TypeVariable tvar) t uctx) types
                types = typeSchemeType . sInferenceResultScheme <$> results
                vars = L.concat (typeSchemeVariables . sInferenceResultScheme <$> results)

    TermLiteral lit -> Flows.pure $ yieldWithoutConstraints $ Types.mono $ TypeLiteral $ literalType lit

    TermMap m -> Flows.bind sNewVar withKeyVar
      where
        withKeyVar kvar = Flows.bind sNewVar withValueVar
          where
            withValueVar vvar = if M.null m
               then Flows.pure $ yield (TypeScheme [kvar, vvar] $ Types.map (TypeVariable kvar) (TypeVariable vvar)) []
               else Flows.map withResults (Flows.sequence $ fmap fromPair $ M.toList m)
              where
                fromPair (k, v) = Flows.bind (sInferTypeInternal k) withKeyType
                  where
                    withKeyType (SInferenceResult (TypeScheme kvars kt) kconstraints) = Flows.map withValueType (sInferTypeInternal v)
                      where
                        withValueType (SInferenceResult (TypeScheme vvars vt) vconstraints)
                          = (kvars ++ vvars,
                             [TypeConstraint (TypeVariable kvar) kt $ Just "map key",
                              TypeConstraint (TypeVariable vvar) vt $ Just "map value"]
                              ++ kconstraints ++ vconstraints)
                withResults pairs = yield (TypeScheme (L.concat (fst <$> pairs)) $ Types.map (TypeVariable kvar) (TypeVariable vvar)) $
                  L.concat (snd <$> pairs)

    TermFunction (FunctionPrimitive name) -> Flow $ \ctx t -> case M.lookup name (sInferenceContextLexicon ctx) of
      Just scheme -> unFlow (Flows.map withoutConstraints $ sInstantiate scheme) ctx t
      Nothing -> unFlow (Flows.fail $ "No such primitive: " ++ unName name) ctx t

    TermProduct els -> if L.null els
      then Flows.pure $ yield (Types.mono $ Types.product []) []
      else Flows.map fromResults (Flows.sequence (sInferTypeInternal <$> els))
      where
        fromResults results = yield (TypeScheme tvars $ TypeProduct tbodies) constraints
          where
            tvars = L.concat $ typeSchemeVariables . sInferenceResultScheme <$> results
            tbodies = typeSchemeType . sInferenceResultScheme <$> results
            constraints = L.concat $ sInferenceResultConstraints <$> results

    TermVariable var -> Flow $ \ctx t -> case M.lookup var (sInferenceContextTypingEnvironment ctx) of
      Just scheme -> unFlow (Flows.map withoutConstraints $ sInstantiate scheme) ctx t
      Nothing -> unFlow (Flows.fail $ "Variable not bound to type: " ++ unName var) ctx t

  where
    unsupported = Flows.fail "Not yet supported"
    yield = SInferenceResult
    yieldWithoutConstraints scheme = yield scheme []
    withoutConstraints scheme = SInferenceResult scheme []

sInstantiate :: TypeScheme -> Flow SInferenceContext TypeScheme
sInstantiate scheme = Flows.map doSubst (sNewVars $ L.length oldVars)
    where
      doSubst newVars = TypeScheme newVars $ sSubstituteTypeVariables subst $ typeSchemeType scheme
        where
          subst = SSubst $ M.fromList $ L.zip oldVars (TypeVariable <$> newVars)
      oldVars = L.intersect (L.nub $ typeSchemeVariables scheme) (sFreeTypeVariables $ typeSchemeType scheme)

sInstantiateAndNormalize :: TypeScheme -> Flow SInferenceContext TypeScheme
sInstantiateAndNormalize scheme = Flows.map sNormalizeTypeVariables (sInstantiate scheme)

sFreeTypeVariables :: Type -> [Name]
sFreeTypeVariables typ = case typ of
  TypeFunction (FunctionType dom cod) -> L.nub $ sFreeTypeVariables dom ++ sFreeTypeVariables cod
  TypeList t -> sFreeTypeVariables t
  TypeLiteral _ -> []
  TypeMap (MapType k v) -> L.nub $ sFreeTypeVariables k ++ sFreeTypeVariables v
  TypeProduct types -> L.nub $ L.concat $ sFreeTypeVariables <$> types
  TypeVariable name -> [name]

sNormalizeTypeVariables :: TypeScheme -> TypeScheme
sNormalizeTypeVariables scheme = TypeScheme newVars $ sSubstituteTypeVariables subst $ typeSchemeType scheme
  where
    normalVariables = (\n -> Name $ "t" ++ show n) <$> [0..]
    oldVars = typeSchemeVariables scheme
    newVars = L.take (L.length oldVars) normalVariables
    subst =SSubst $ M.fromList $ L.zip oldVars (TypeVariable <$> newVars)

sNewVar :: Flow SInferenceContext Name
sNewVar = Flows.map L.head (sNewVars 1)

sNewVars :: Int -> Flow SInferenceContext [Name]
sNewVars n = Flow helper
  where
    helper ctx t = FlowState value ctx' t
      where
        value = Just ((\n -> Name $ "t" ++ show n) <$> (L.take n [(sInferenceContextVariableCount ctx)..]))
        ctx' = ctx {sInferenceContextVariableCount = n + sInferenceContextVariableCount ctx}

sVarScheme :: Name -> TypeScheme
sVarScheme v = TypeScheme [v] $ TypeVariable v

-- | Temporarily add a (term variable, type scheme) to the typing environment
sWithTypeBinding :: Name -> TypeScheme -> Flow SInferenceContext a -> Flow SInferenceContext a
sWithTypeBinding name scheme f = Flow helper
  where
    helper ctx0 t0 = FlowState e ctx3 t1
      where
        env = sInferenceContextTypingEnvironment ctx0
        ctx1 = ctx0 {sInferenceContextTypingEnvironment = M.insert name scheme env}
        FlowState e ctx2 t1 = unFlow f ctx1 t0
        ctx3 = ctx2 {sInferenceContextTypingEnvironment = env}


--------------------------------------------------------------------------------
-- Testing

_app l r = TermApplication $ Application l r
_int = TermLiteral . LiteralInteger . IntegerValueInt32
_lambda v b = TermFunction $ FunctionLambda $ Lambda (Name v) Nothing b
_list = TermList
_map = TermMap
_pair l r = TermProduct [l, r]
_str = TermLiteral . LiteralString
_var = TermVariable . Name

(@@) :: Term -> Term -> Term
f @@ x = TermApplication $ Application f x

infixr 0 >:
(>:) :: String -> Term -> (Name, Term)
n >: t = (Name n, t)

int32 = TermLiteral . LiteralInteger . IntegerValueInt32
lambda v b = TermFunction $ FunctionLambda $ Lambda (Name v) Nothing b
list = TermList
map = TermMap
pair l r = TermProduct [l, r]
string = TermLiteral . LiteralString
var = TermVariable . Name
with env bindings = L.foldl (\e (k, v) -> TermLet $ Let [LetBinding k v Nothing] e) env bindings




infixr 0 ===
(===) :: Type -> Type -> TypeConstraint
t1 === t2 = TypeConstraint t1 t2 $ Just "some context"


_add = TermFunction $ FunctionPrimitive $ Name "add"
primPred = TermFunction $ FunctionPrimitive $ Name "primPred"
primSucc = TermFunction $ FunctionPrimitive $ Name "primSucc"

_unify t1 t2 = uUnify [TypeConstraint t1 t2 $ Just "ctx"]

sTestLexicon = M.fromList [
  (Name "add", Types.mono $ Types.function Types.int32 Types.int32),
  (Name "primPred", Types.mono $ Types.function Types.int32 Types.int32),
  (Name "primSucc", Types.mono $ Types.function Types.int32 Types.int32)]

sInitialContext = SInferenceContext sTestLexicon 0 M.empty

_infer term = flowStateValue $ unFlow (sInferType term) sInitialContext Tier1.emptyTrace

_inferRaw term = flowStateValue $ unFlow (sInferTypeInternal term) sInitialContext Tier1.emptyTrace

_instantiate scheme = flowStateValue $ unFlow (sInstantiate scheme) sInitialContext Tier1.emptyTrace

_con t1 t2 = TypeConstraint t1 t2 $ Just "ctx"



{-

----------------------------------------
-- Unification

_unify (Types.var "a") (Types.var "b")
_unify (Types.var "a") (Types.list $ Types.var "b")

_unify (Types.var "a") Types.int32

_unify (Types.list Types.string) (Types.list $ Types.var "a")

_unify (Types.map (Types.var "a") Types.int32) (Types.map Types.string (Types.var "b"))

sUnifyAll [Types.var "t1" === Types.var "t0", Types.var "t1" === Types.int32]

-- Failure cases

_unify Types.string Types.int32

_unify (Types.var "a") (Types.list $ Types.var "a")

ctx = Just "ctx"
uUnify [(TypeConstraint (Types.var "t1") (Types.var "t0") ctx), (TypeConstraint (Types.var "t1") Types.int32 ctx)]


----------------------------------------
-- Inference

_infer $ _str "hello"

_infer _add

_infer $ _list [_add]

_infer $ _lambda "x" $ _int 42

_infer $ _list [_int 42]




_inferRaw (_list [_int 42])



:module
import Hydra.NewInference

-- System F cases
_inferRaw (lambda "x" $ var "x")                                                           -- (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.var "t0"))
_inferRaw (int32 32 `with` ["foo">: lambda "x" $ var "x"])                                 -- (Types.mono Types.int32)
_inferRaw ((var "f" @@ int32 0) `with` ["f">: lambda "x" $ var "x"])                       -- (Types.mono Types.int32)
_inferRaw (var "f" `with` ["f">: (lambda "x" $ var "x") @@ int32 0])                       -- (Types.mono Types.int32)
_inferRaw (lambda "x" $ list [var "x"])                                                    -- (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))
_inferRaw (var "sng" `with` ["sng">: lambda "x" $ list [var "x"]])                         -- (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))
_inferRaw ((var "+" @@ (primSucc @@ (primSucc @@ int32 0)) @@ (primSucc @@ int32 0)) `with` ["+">: lambda "x" $ lambda "y" (primSucc @@ (var "+" @@ (primPred @@ var "x") @@ var "y"))]) -- (Types.mono Types.int32)
_inferRaw (var "f" `with` ["f">: lambda "x" $ lambda "y" (var "f" @@ int32 0 @@ var "x")]) -- (Types.poly ["t0"] $ Types.function Types.int32 (Types.function Types.int32 (Types.var "t0")))



_inferRaw (var "f" `with` ["f">: lambda "x" $ lambda "y" (var "f" @@ int32 0 @@ var "x")]) -- (Types.poly ["t0"] $ Types.function Types.int32 (Types.function Types.int32 (Types.var "t0")))



:set +m

constraints = [
  _con (Types.function (Types.var "t5") (Types.var "t6")) (Types.var "t0"),
  _con (Types.var "t5") Types.int32,
  _con (Types.function (Types.var "t3") (Types.var "t4")) (Types.var "t6"),
  _con (Types.var "t3") (Types.var "t1"),
  _con (Types.var "t0") (Types.function (Types.var "t1") (Types.function (Types.var "t2") (Types.var "t4")))]

uUnify constraints




_inferRaw (_lambda "x" $ _list [_var "x", _int 42])

_inferRaw (_lambda "y" (_a (_lambda "x" $ _list [_var "x"]) (_var "y")))




_inferRaw (var "sng" `with` ["sng">: lambda "x" $ list [var "x"]])


----------------------------------------
-- Instantiation

sInstantiate (Types.poly ["t0", "t1"] $ Types.function (Types.var "t1") (Types.var "t1")) sInitialContext



-}