Skip to content

Commit

Permalink
Subsume type signatures with expression types
Browse files Browse the repository at this point in the history
  • Loading branch information
Oskar Wickström committed Jan 24, 2016
1 parent 93a563a commit 5e8188e
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 84 deletions.
29 changes: 18 additions & 11 deletions src/oden/Oden/Backend/Go.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ func name arg returnType body =
<+> returnType
<+> if isEmpty body then empty else block body

var :: Name -> Expr Mono.Type -> Doc
var name expr =
varWithType :: Name -> Mono.Type -> Expr Mono.Type -> Doc
varWithType name mt expr =
text "var"
<+> safeName name
<+> codegenType (typeOf expr)
<+> codegenType mt
<+> equals
<+> codegenExpr expr

var :: Name -> Expr Mono.Type -> Doc
var name expr = varWithType name (typeOf expr) expr

return' :: Expr Mono.Type -> Doc
return' e@(Application _ _ t) | t == Mono.typeUnit =
codegenExpr e $+$ text "return"
Expand Down Expand Up @@ -140,21 +143,25 @@ codegenExpr (If condExpr thenExpr elseExpr t) =
codegenExpr (Slice exprs t) = codegenType t
<> braces (hcat (punctuate (comma <+> space) (map codegenExpr exprs)))

codegenTopLevel :: Name -> Expr Mono.Type -> Doc
codegenTopLevel name (NoArgFn body (Mono.TNoArgFn r)) =
codegenTopLevel :: Name -> Mono.Type -> Expr Mono.Type -> Doc
codegenTopLevel name (Mono.TNoArgFn r) (NoArgFn body _) =
func (safeName name) empty (codegenType r) (return' body)
codegenTopLevel name (Fn a body (Mono.TFn d r)) =
codegenTopLevel name _ (NoArgFn body (Mono.TNoArgFn r)) =
func (safeName name) empty (codegenType r) (return' body)
codegenTopLevel name (Mono.TFn d r) (Fn a body _) =
func (safeName name) (funcArg a d) (codegenType r) (return' body)
codegenTopLevel name _ (Fn a body (Mono.TFn d r)) =
func (safeName name) (funcArg a d) (codegenType r) (return' body)
codegenTopLevel name expr =
var name expr
codegenTopLevel name t expr =
varWithType name t expr

codegenInstance :: InstantiatedDefinition -> Doc
codegenInstance (InstantiatedDefinition name expr) =
codegenTopLevel name expr
codegenTopLevel name (typeOf expr) expr

codegenMonomorphed :: MonomorphedDefinition -> Doc
codegenMonomorphed (MonomorphedDefinition name expr) =
codegenTopLevel name expr
codegenMonomorphed (MonomorphedDefinition name mt expr) =
codegenTopLevel name mt expr

codegenImport :: Import -> Doc
codegenImport (Import name) =
Expand Down
12 changes: 7 additions & 5 deletions src/oden/Oden/Compiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Oden.Scope as Scope
import qualified Oden.Type.Monomorphic as Mono
import qualified Oden.Type.Polymorphic as Poly

data MonomorphedDefinition = MonomorphedDefinition Name (Core.Expr Mono.Type)
data MonomorphedDefinition = MonomorphedDefinition Name Mono.Type (Core.Expr Mono.Type)
deriving (Show, Eq, Ord)

data InstantiatedDefinition =
Expand Down Expand Up @@ -198,11 +198,13 @@ unwrapLetInstances [] body = body
unwrapLetInstances (LetInstance mn me:is) body = Core.Let mn me (unwrapLetInstances is body) (Core.typeOf body)

monomorphDefinition :: Core.Definition -> Monomorph ()
monomorphDefinition d@(Core.Definition name (s, expr)) = do
monomorphDefinition d@(Core.Definition name (Poly.Forall _ st, expr)) = do
addToScope d
unless (Poly.isPolymorphic s) $ do
mExpr <- monomorph expr
addMonomorphed name (MonomorphedDefinition name mExpr)
case Poly.toMonomorphic st of
Left _ -> return ()
Right mt -> do
mExpr <- monomorph expr
addMonomorphed name (MonomorphedDefinition name mt mExpr)

monomorphPackage :: Scope -> Core.Package -> Either CompilationError CompiledPackage
monomorphPackage scope' (Core.Package name imports definitions) = do
Expand Down
73 changes: 12 additions & 61 deletions src/oden/Oden/Infer.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}

module Oden.Infer (
Expand All @@ -12,18 +11,22 @@ module Oden.Infer (
constraintsExpr
) where

import Control.Arrow (left)
import Control.Monad.Except
import Control.Monad.Identity
import Control.Monad.RWS hiding ((<>))

import Data.List (nub)
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Set as Set

import qualified Oden.Core as Core
import qualified Oden.Core.Untyped as Untyped
import Oden.Env as Env
import Oden.Identifier
import Oden.Infer.Substitution
import Oden.Infer.Subsumption
import Oden.Type.Polymorphic

-------------------------------------------------------------------------------
Expand Down Expand Up @@ -53,70 +56,27 @@ type Unifier = (Subst, [Constraint])
-- | Constraint solver monad
type Solve a = ExceptT TypeError Identity a

newtype Subst = Subst (Map.Map TVar Type)
deriving (Eq, Ord, Show, Monoid)

class FTV a => Substitutable a where
apply :: Subst -> a -> a

instance Substitutable Type where
apply _ TAny = TAny
apply _ (TCon a) = TCon a
apply (Subst s) t@(TVar a) = Map.findWithDefault t a s
apply s (TNoArgFn t) = TNoArgFn (apply s t)
apply s (t1 `TFn` t2) = apply s t1 `TFn` apply s t2
apply s (TUncurriedFn as r) = TUncurriedFn (map (apply s) as) (apply s r)
apply s (TVariadicFn as v r) = TVariadicFn (map (apply s) as) (apply s v) (apply s r)
apply s (TSlice t) = TSlice (apply s t)


instance Substitutable Scheme where
apply (Subst s) (Forall as t) = Forall as $ apply s' t
where s' = Subst $ foldr Map.delete s as

instance FTV Core.CanonicalExpr where
ftv (sc, expr) = ftv sc `Set.union` ftv expr

instance Substitutable Core.CanonicalExpr where
apply s (sc, expr) = (apply s sc, apply s expr)

instance FTV Constraint where
ftv (t1, t2) = ftv t1 `Set.union` ftv t2

instance Substitutable Constraint where
apply s (t1, t2) = (apply s t1, apply s t2)

instance Substitutable a => Substitutable [a] where
apply = map . apply

instance FTV Env where
ftv (TypeEnv env) = ftv $ Map.elems env

instance Substitutable Env where
apply s (TypeEnv env) = TypeEnv $ Map.map (apply s) env

instance FTV (Core.Expr Type) where
ftv = ftv . Core.typeOf

instance Substitutable (Core.Expr Type) where
apply s (Core.Symbol x t) = Core.Symbol x (apply s t)
apply s (Core.Application f p t) = Core.Application (apply s f) (apply s p) (apply s t)
apply s (Core.NoArgApplication f t) = Core.NoArgApplication (apply s f) (apply s t)
apply s (Core.UncurriedFnApplication f p t) = Core.UncurriedFnApplication (apply s f) (apply s p) (apply s t)
apply s (Core.Fn x b t) = Core.Fn x (apply s b) (apply s t)
apply s (Core.NoArgFn b t) = Core.NoArgFn (apply s b) (apply s t)
apply s (Core.Let x e b t) = Core.Let x (apply s e) (apply s b) (apply s t)
apply s (Core.Literal l t) = Core.Literal l (apply s t)
apply s (Core.If c tb fb t) = Core.If (apply s c) (apply s tb) (apply s fb) (apply s t)
apply s (Core.Slice es t) = Core.Slice (apply s es) (apply s t)

data TypeError
= UnificationFail Type Type
| InfiniteType TVar Type
| NotInScope Identifier
| Ambigious [Constraint]
| UnificationMismatch [Type] [Type]
| ArgumentCountMismatch [Type] [Type]
| TypeSignatureSubsumptionError Name SubsumptionError
deriving (Show, Eq)

-------------------------------------------------------------------------------
Expand Down Expand Up @@ -268,24 +228,24 @@ infer expr = case expr of
return (Core.Slice tes (TSlice tv))

inferDef :: Untyped.Definition -> Infer Core.Definition
inferDef (Untyped.Definition name (Just sc) expr) = do
te <- inEnv (Unqualified name, sc) (infer expr)
return (Core.Definition name (sc, te))
inferDef (Untyped.Definition name Nothing expr) = do
inferDef (Untyped.Definition name s expr) = do
tv <- fresh
env <- ask
te <- inEnv (Unqualified name, Forall [] tv) (infer expr)
let recType = fromMaybe (Forall [] tv) s
te <- inEnv (Unqualified name, recType) (infer expr)
return (Core.Definition name (generalize env te))

inferDefinition :: Env -> Untyped.Definition -> Either TypeError Core.Definition
inferDefinition env def@(Untyped.Definition _ Nothing _) = do
(Core.Definition name (_, te), cs) <- runInfer env (inferDef def)
subst <- runSolve cs
return $ Core.Definition name (closeOver (apply subst te))
inferDefinition env def = do
inferDefinition env def@(Untyped.Definition _ (Just st) _) = do
(Core.Definition name ce, cs) <- runInfer env (inferDef def)
subst <- runSolve cs
return $ Core.Definition name (apply subst ce)
let (Forall _ _, substExpr) = apply subst ce
ce' <- left (TypeSignatureSubsumptionError name) $ subsumeTypeSignature st substExpr
return $ Core.Definition name ce'

inferPackage :: Env -> Untyped.Package -> Either TypeError (Core.Package, Env)
inferPackage env (Untyped.Package name imports defs) = do
Expand Down Expand Up @@ -327,14 +287,6 @@ normalize (Forall _ body, te) = (Forall (map snd ord) (normtype body), te)
-- Constraint Solver
-------------------------------------------------------------------------------

-- | The empty substitution
emptySubst :: Subst
emptySubst = mempty

-- | Compose substitutions
compose :: Subst -> Subst -> Subst
(Subst s1) `compose` (Subst s2) = Subst $ Map.map (apply (Subst s1)) s2 `Map.union` s1

-- | Run the constraint solver
runSolve :: [Constraint] -> Either TypeError Subst
runSolve cs = runIdentity $ runExceptT $ solver st
Expand All @@ -353,7 +305,6 @@ unifies t1 t2 | t1 == t2 = return emptySubst
unifies TAny (TVar v) = v `bind` TAny
unifies (TVar v) TAny = v `bind` TAny
unifies TAny _ = return emptySubst
unifies _ TAny = return emptySubst
unifies (TVar v) t = v `bind` t
unifies t (TVar v) = v `bind` t
unifies (TFn t1 t2) (TFn t3 t4) = unifyMany [t1, t2] [t3, t4]
Expand Down
63 changes: 63 additions & 0 deletions src/oden/Oden/Infer/Substitution.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Oden.Infer.Substitution where

import Oden.Type.Polymorphic
import Oden.Core as Core

import qualified Data.Set as Set
import qualified Data.Map as Map

newtype Subst = Subst (Map.Map TVar Type)
deriving (Eq, Ord, Show, Monoid)

-- | The empty substitution
emptySubst :: Subst
emptySubst = mempty

-- | Compose substitutions
compose :: Subst -> Subst -> Subst
(Subst s1) `compose` (Subst s2) = Subst $ Map.map (apply (Subst s1)) s2 `Map.union` s1

class FTV a => Substitutable a where
apply :: Subst -> a -> a

instance Substitutable Type where
apply _ TAny = TAny
apply _ (TCon a) = TCon a
apply (Subst s) t@(TVar a) = Map.findWithDefault t a s
apply s (TNoArgFn t) = TNoArgFn (apply s t)
apply s (t1 `TFn` t2) = apply s t1 `TFn` apply s t2
apply s (TUncurriedFn as r) = TUncurriedFn (map (apply s) as) (apply s r)
apply s (TVariadicFn as v r) = TVariadicFn (map (apply s) as) (apply s v) (apply s r)
apply s (TSlice t) = TSlice (apply s t)


instance Substitutable Scheme where
apply (Subst s) (Forall as t) = Forall as $ apply s' t
where s' = Subst $ foldr Map.delete s as

instance FTV Core.CanonicalExpr where
ftv (sc, expr) = ftv sc `Set.union` ftv expr

instance Substitutable Core.CanonicalExpr where
apply s (sc, expr) = (apply s sc, apply s expr)

instance FTV (Core.Expr Type) where
ftv = ftv . Core.typeOf

instance Substitutable (Core.Expr Type) where
apply s (Core.Symbol x t) = Core.Symbol x (apply s t)
apply s (Core.Application f p t) = Core.Application (apply s f) (apply s p) (apply s t)
apply s (Core.NoArgApplication f t) = Core.NoArgApplication (apply s f) (apply s t)
apply s (Core.UncurriedFnApplication f p t) = Core.UncurriedFnApplication (apply s f) (apply s p) (apply s t)
apply s (Core.Fn x b t) = Core.Fn x (apply s b) (apply s t)
apply s (Core.NoArgFn b t) = Core.NoArgFn (apply s b) (apply s t)
apply s (Core.Let x e b t) = Core.Let x (apply s e) (apply s b) (apply s t)
apply s (Core.Literal l t) = Core.Literal l (apply s t)
apply s (Core.If c tb fb t) = Core.If (apply s c) (apply s tb) (apply s fb) (apply s t)
apply s (Core.Slice es t) = Core.Slice (apply s es) (apply s t)

instance Substitutable a => Substitutable [a] where
apply = map . apply
69 changes: 69 additions & 0 deletions src/oden/Oden/Infer/Subsumption.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module Oden.Infer.Subsumption (
Subsuming,
SubsumptionError(..),
subsume,
subsumeTypeSignature
) where

import Oden.Type.Polymorphic
import Oden.Core as Core
import Oden.Infer.Substitution

import qualified Data.Map as Map

data SubsumptionError = SubsumptionError Type Type
deriving (Show, Eq)

class Subsuming s where
subsume :: s -> s -> Either SubsumptionError s

subsumeTypeSignature :: Scheme -> Core.Expr Type -> Either SubsumptionError Core.CanonicalExpr
subsumeTypeSignature s@(Forall _ st) expr = do
subst <- getSubst st (Core.typeOf expr)
return (s, apply subst expr)
where
getSubst :: Type -> Type -> Either SubsumptionError Subst
getSubst t (TVar tv) = return (Subst (Map.singleton tv t))
getSubst (TFn a1 r1) (TFn a2 r2) = do
a <- getSubst a1 a2
r <- getSubst r1 r2
return (a `compose` r)
getSubst (TNoArgFn r1) (TNoArgFn r2) = getSubst r1 r2
getSubst (TUncurriedFn a1 r1) (TUncurriedFn a2 r2) = do
as <- mapM (uncurry getSubst) ((r1, r2) : zip a1 a2)
return (foldl compose emptySubst as)
getSubst (TVariadicFn a1 v1 r1) (TVariadicFn a2 v2 r2) = do
as <- mapM (uncurry getSubst) ((r1, r2) : (v1, v2) : zip a1 a2)
return (foldl compose emptySubst as)
getSubst TAny _ = return emptySubst
getSubst t1 t2
| t1 == t2 = return emptySubst
| otherwise = Left (SubsumptionError t1 t2)

instance Subsuming Type where
TAny `subsume` TAny = Right TAny
t `subsume` TAny = Left (SubsumptionError t TAny)
TAny `subsume` _ = Right TAny
t1@(TNoArgFn at1) `subsume` (TNoArgFn at2) = do
_ <- at1 `subsume` at2
return t1
t1@(TFn at1 rt1) `subsume` (TFn at2 rt2) = do
_ <- at1 `subsume` at2
_ <- rt1 `subsume` rt2
return t1
t1@(TUncurriedFn ats1 rt1) `subsume` (TUncurriedFn ats2 rt2) = do
mapM_ (uncurry subsume) (zip ats1 ats2)
_ <- rt1 `subsume` rt2
return t1
t1@(TVariadicFn ats1 vt1 rt1) `subsume` (TVariadicFn ats2 vt2 rt2) = do
mapM_ (uncurry subsume) (zip ats1 ats2)
_ <- vt1 `subsume` vt2
_ <- rt1 `subsume` rt2
return t1
t1@(TSlice st1) `subsume` (TSlice st2) = do
_ <- st1 `subsume` st2
return t1
t1 `subsume` t2
| t1 == t2 = Right t1
| otherwise = Left (SubsumptionError t1 t2)

Loading

0 comments on commit 5e8188e

Please sign in to comment.