diff --git a/lambda-buffers-compiler/lambda-buffers-compiler.cabal b/lambda-buffers-compiler/lambda-buffers-compiler.cabal index f23d51cb..538654ff 100644 --- a/lambda-buffers-compiler/lambda-buffers-compiler.cabal +++ b/lambda-buffers-compiler/lambda-buffers-compiler.cabal @@ -114,6 +114,9 @@ library LambdaBuffers.Compiler.NamingCheck LambdaBuffers.Compiler.ProtoCompat LambdaBuffers.Compiler.ProtoCompat.Types + LambdaBuffers.Compiler.TypeClass.Pat + LambdaBuffers.Compiler.TypeClass.Pretty + LambdaBuffers.Compiler.TypeClass.Rules LambdaBuffers.Compiler.TypeClassCheck hs-source-dirs: src diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat.hs index 4848f5a6..0e0a5bed 100644 --- a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat.hs +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat.hs @@ -54,10 +54,10 @@ throwNamingError = either (Left . NamingError) return -- TODO(bladyjoker): Revisit and make part of compiler.proto data ProtoError - = MultipleInstanceHeads ClassName [Ty] SourceInfo - | NoInstanceHead ClassName SourceInfo - | NoConstraintArgs ClassName SourceInfo - | MultipleConstraintArgs ClassName [Ty] SourceInfo + = MultipleInstanceHeads TyClassRef [Ty] SourceInfo + | NoInstanceHead TyClassRef SourceInfo + | NoConstraintArgs TyClassRef SourceInfo + | MultipleConstraintArgs TyClassRef [Ty] SourceInfo | NoClassArgs ClassName SourceInfo | MultipleClassArgs ClassName SourceInfo | NoTyAppArgs SourceInfo @@ -387,6 +387,41 @@ instance IsMessage P.Product'Record'Field Field where Classes, instances, constraints -} +instance IsMessage P.TyClassRef'Local LocalClassRef where + fromProto lr = do + si <- fromProto $ lr ^. P.sourceInfo + nm <- fromProto $ lr ^. P.className + pure $ LocalClassRef nm si + + toProto (LocalClassRef nm si) = + defMessage + & P.className .~ toProto nm + & P.sourceInfo .~ toProto si + +instance IsMessage P.TyClassRef'Foreign ForeignClassRef where + fromProto fr = do + si <- fromProto $ fr ^. P.sourceInfo + mn <- fromProto $ fr ^. P.moduleName + tn <- fromProto $ fr ^. P.className + pure $ ForeignClassRef tn mn si + + toProto (ForeignClassRef tn mn si) = + defMessage + & P.className .~ toProto tn + & P.moduleName .~ toProto mn + & P.sourceInfo .~ toProto si + +instance IsMessage P.TyClassRef TyClassRef where + fromProto tr = case tr ^. P.maybe'classRef of + Nothing -> throwProtoError $ OneOfNotSet "class_ref" + Just x -> case x of + P.TyClassRef'LocalClassRef lr -> LocalCI <$> fromProto lr + P.TyClassRef'ForeignClassRef f -> ForeignCI <$> fromProto f + + toProto = \case + LocalCI lr -> defMessage & P.localClassRef .~ toProto lr + ForeignCI fr -> defMessage & P.foreignClassRef .~ toProto fr + instance IsMessage P.ClassDef ClassDef where fromProto cd = do si <- fromProto $ cd ^. P.sourceInfo @@ -411,7 +446,7 @@ instance IsMessage P.ClassDef ClassDef where instance IsMessage P.InstanceClause InstanceClause where fromProto ic = do si <- fromProto $ ic ^. P.sourceInfo - cnm <- fromProto $ ic ^. P.className + cnm <- fromProto $ ic ^. P.classRef csts <- traverse fromProto $ ic ^. P.constraints hds <- ic ^. (P.heads . traversing fromProto) hd <- case hds of @@ -422,7 +457,7 @@ instance IsMessage P.InstanceClause InstanceClause where toProto (InstanceClause cnm hd csts si) = defMessage - & P.className .~ toProto cnm + & P.classRef .~ toProto cnm & P.heads .~ pure (toProto hd) & P.constraints .~ (toProto <$> csts) & P.sourceInfo .~ toProto si @@ -430,7 +465,7 @@ instance IsMessage P.InstanceClause InstanceClause where instance IsMessage P.Constraint Constraint where fromProto c = do si <- fromProto $ c ^. P.sourceInfo - cnm <- fromProto $ c ^. P.className + cnm <- fromProto $ c ^. P.classRef args <- c ^. (P.arguments . traversing fromProto) arg <- case args of [] -> throwProtoError $ NoConstraintArgs cnm si @@ -440,7 +475,7 @@ instance IsMessage P.Constraint Constraint where toProto (Constraint cnm arg si) = defMessage - & P.className .~ toProto cnm + & P.classRef .~ toProto cnm & P.arguments .~ pure (toProto arg) & P.sourceInfo .~ toProto si @@ -454,15 +489,17 @@ instance IsMessage P.Module Module where tdefs <- traverse fromProto $ m ^. P.typeDefs cdefs <- traverse fromProto $ m ^. P.classDefs insts <- traverse fromProto $ m ^. P.instances + impts <- traverse fromProto $ m ^. P.imports si <- fromProto $ m ^. P.sourceInfo - pure $ Module mnm tdefs cdefs insts si + pure $ Module mnm tdefs cdefs insts impts si - toProto (Module mnm tdefs cdefs insts si) = + toProto (Module mnm tdefs cdefs insts impts si) = defMessage & P.moduleName .~ toProto mnm & P.typeDefs .~ (toProto <$> tdefs) & P.classDefs .~ (toProto <$> cdefs) & P.instances .~ (toProto <$> insts) + & P.imports .~ (toProto <$> impts) & P.sourceInfo .~ toProto si instance IsMessage P.CompilerInput CompilerInput where diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat/Types.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat/Types.hs index d38f8166..d00992fa 100644 --- a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat/Types.hs +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat/Types.hs @@ -14,6 +14,7 @@ module LambdaBuffers.Compiler.ProtoCompat.Types ( Field (..), FieldName (..), ForeignRef (..), + ForeignClassRef (..), InstanceClause (..), Kind (..), KindRefType (..), @@ -21,6 +22,7 @@ module LambdaBuffers.Compiler.ProtoCompat.Types ( KindType (..), LBName (..), LocalRef (..), + LocalClassRef (..), Module (..), ModuleName (..), ModuleNamePart (..), @@ -35,6 +37,7 @@ module LambdaBuffers.Compiler.ProtoCompat.Types ( TyApp (..), TyArg (..), TyBody (..), + TyClassRef (..), TyDef (..), TyName (..), TyRef (..), @@ -181,6 +184,21 @@ data Product | TupleI Tuple deriving stock (Show, Eq, Ord, Generic) +data ForeignClassRef = ForeignClassRef + { className :: ClassName + , moduleName :: ModuleName + , sourceInfo :: SourceInfo + } + deriving stock (Show, Eq, Ord, Generic) + +data LocalClassRef = LocalClassRef {className :: ClassName, sourceInfo :: SourceInfo} + deriving stock (Show, Eq, Ord, Generic) + +data TyClassRef + = LocalCI LocalClassRef + | ForeignCI ForeignClassRef + deriving stock (Show, Eq, Ord, Generic) + data ClassDef = ClassDef { className :: ClassName , classArgs :: TyArg @@ -191,7 +209,7 @@ data ClassDef = ClassDef deriving stock (Show, Eq, Ord, Generic) data InstanceClause = InstanceClause - { className :: ClassName + { classRef :: TyClassRef , head :: Ty , constraints :: [Constraint] , sourceInfo :: SourceInfo @@ -199,7 +217,7 @@ data InstanceClause = InstanceClause deriving stock (Show, Eq, Ord, Generic) data Constraint = Constraint - { className :: ClassName + { classRef :: TyClassRef , argument :: Ty , sourceInfo :: SourceInfo } @@ -210,6 +228,7 @@ data Module = Module , typeDefs :: [TyDef] , classDefs :: [ClassDef] , instances :: [InstanceClause] + , imports :: [ModuleName] , sourceInfo :: SourceInfo } deriving stock (Show, Eq, Ord, Generic) diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pat.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pat.hs new file mode 100644 index 00000000..c8407941 --- /dev/null +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pat.hs @@ -0,0 +1,104 @@ +{-# LANGUAGE LambdaCase #-} + +module LambdaBuffers.Compiler.TypeClass.Pat ( + Pat (..), + toProd, + toRec, + toSum, + patList, + matches, +) where + +import Data.Text (Text) + +{- A simple ADT to represent patterns. + +Note that this ADT allows us to represent nonsensical types (i.e. we can "put the wrong pattern in a hole"). +This could be ameliorated by using a GADT, which would give us correct-by-construction patterns at the +cost of significantly more complex type signatures. +-} + +data Pat + = {- extremely stupid, unfortunately necessary -} + Name Text + | ModuleName [Text] -- also stupid, also necessary -_- + | Opaque + | {- Lists (constructed from Nil and :*) with bare types are used to + encode products (where a list of length n encodes an n-tuple) + Lists with field labels (l := t) are used to encode records and sum types + These representations let us "peer into the structure" of the TyBody, and are + somewhat analogous to the Generics.SOP representation or, in the case of records (or sums + interpreted as variants), to a row-types representation. We can imagine that each record and + sum are backed by an implicit row. + Unfortunately this encoding allows us to generate Pats which do not correspond to + any possible types. For the purposes of instance resolution/code generation this shouldn't matter + so long as the patterns are only generalizations of "real" types. We could ameliorate this problem by + using a GADT for Pat, but this would greatly complicate the constraint solving/deriving + algorithms and require copious use of type families (and possibly singletons). + -} + Nil -- Nil and :* are hacks to write rules for ProdP and SumP. A bare Nil == Unit + | Pat :* Pat -- cons + | Pat := Pat {- field labels or constr names. The LHS should be (Name "Foo") + for schema types, but should be a PatVar for deriving rules and instances -} + | RecP Pat {- where the Pat arg is expected to be (l := t :* rest) or Nil, where rest + is also a pat-list of labeled fields or Nil -} + | ProdP Pat {- Pat arg should be a list of "Bare types" -} + | SumP Pat {- where the Pat arg is expected to be (Constr l t :* rest) or Nil, where + rest is either Nil or a tyList of Constrs -} + | VarP Text {- This isn't a type variable. Although it is used to represent them in certain contexts, + it is also used more generally to refer to any "hole" in a pattern to which another pattern + may be substituted. We could have separate constr for type variables but it doesn't appear to be + necessary at this time. -} + | RefP Pat Pat {- 1st arg should be a ModuleName -} + | AppP Pat Pat {- Pattern for Type applications -} + | {- This last one is a bit special. This represents a complete type declaration. + The first Pat should be instantiated to `Name l` where l is a concrete name. + The second Pat should be instantiated to a Pat-List (using :*/Nil) which only contains Names. + The final Pat should be instantiated to a Pat body. + In some languages, parts of this may be ignored. E.g. in Rust the type name doesn't matter (we use the constr name of the + outermost inner sum for constructing types). -} + DecP Pat Pat Pat + deriving stock (Show, Eq, Ord) + +infixr 5 :* + +{- Utility functions. Turn a list of types into a product/record/sum type. +-} +toProd :: [Pat] -> Pat +toProd = ProdP . foldr (:*) Nil + +toRec :: [Pat] -> Pat +toRec = RecP . foldr (:*) Nil + +toSum :: [Pat] -> Pat +toSum = SumP . foldr (:*) Nil + +{- Converts a pattern that consists of a well formed pattern list + (i.e. patterns formed from :* and Nil) into a list of patterns. +-} +patList :: Pat -> Maybe [Pat] +patList = \case + Nil -> Just [] + p1 :* p2 -> (p1 :) <$> patList p2 + _ -> Nothing + +{- This is used as a predicate to filter instances or Gens which are structurally compatible + with the argument type. + The first argument is the inner Pat from an instance head or Gen. + The second argument is the Pat representation of a type that we want to derive an instance / generate code for. + NOTE: Is not bidirectional! The first Pat has to be more general than the first + (more specifically: The second Pat should be a substitution instance of the first) +-} +matches :: Pat -> Pat -> Bool +matches t1 t2 | t1 == t2 = True -- need the guard +matches (VarP _) _ = True +matches (x :* xs) (x' :* xs') = matches x x' && matches xs xs' +matches (l := t) (l' := t') = matches l l' && matches t t' +matches (ProdP xs) (ProdP xs') = matches xs xs' +matches (RecP xs) (RecP xs') = matches xs xs' +matches (SumP xs) (SumP xs') = matches xs xs' +matches (AppP t1 t2) (AppP t1' t2') = matches t1 t1' && matches t2 t2' +matches (RefP mn t1) (RefP mn' t2) = matches mn mn' && matches t1 t2 +matches (DecP t1 t2 t3) (DecP t1' t2' t3') = + matches t1 t1' && matches t2 t2' && matches t3 t3' +matches _ _ = False diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pretty.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pretty.hs new file mode 100644 index 00000000..a9a94107 --- /dev/null +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Pretty.hs @@ -0,0 +1,141 @@ +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +-- orphans are the whole point of this module! +{-# OPTIONS_GHC -Wno-orphans #-} + +module LambdaBuffers.Compiler.TypeClass.Pretty ( + spaced, + pointies, + (), + (), +) where + +import Control.Lens ((^.)) +import Data.Generics.Labels () +import LambdaBuffers.Compiler.ProtoCompat qualified as P +import LambdaBuffers.Compiler.TypeClass.Pat (Pat (AppP, DecP, ModuleName, Name, Nil, Opaque, ProdP, RecP, RefP, SumP, VarP, (:*), (:=)), patList) +import LambdaBuffers.Compiler.TypeClass.Rules ( + Class (Class), + Constraint (C), + Instance, + Rule ((:<=)), + ) +import Prettyprinter ( + Doc, + Pretty (pretty), + braces, + encloseSep, + hcat, + line, + list, + nest, + parens, + punctuate, + (<+>), + ) + +instance Pretty P.TyClassRef where + pretty = \case + P.ForeignCI (P.ForeignClassRef cn mn _) -> pretty mn <> "." <> pretty (cn ^. #name) + P.LocalCI (P.LocalClassRef cn _) -> pretty (cn ^. #name) + +instance Pretty P.ModuleName where + pretty (P.ModuleName pts _) = hcat . punctuate "." $ map (\x -> pretty $ x ^. #name) pts + +instance Pretty Class where + pretty (Class nm _) = pretty nm + +instance Pretty Constraint where + pretty (C cls p) = pretty cls <+> pretty p + +instance Pretty Instance where + pretty (c :<= []) = pretty c + pretty (c :<= cs) = pretty c <+> "<=" <+> list (pretty <$> cs) + +instance Pretty P.SourcePosition where + pretty (P.SourcePosition col row) = pretty row <> ":" <> pretty col + +instance Pretty P.SourceInfo where + pretty (P.SourceInfo fname f t) = + pretty fname <+> pretty f <> "-" <> pretty t + +-- pretty should emit valid Haskell for well-formed DecPs +instance Pretty Pat where + pretty = \case + Name t -> pretty t + ModuleName ts -> hcat . punctuate "." . map pretty $ ts + Opaque -> "" + RecP ps -> case patList ps of + Nothing -> pretty ps + Just fields -> case traverse prettyField fields of + Just fs -> braces . nest 2 . hcat . punctuate ", " $ fs + Nothing -> pretty ps + ProdP Nil -> "" + ProdP xs -> case patList xs of + Just [f] -> pretty f + Just fs -> parens . hcat . punctuate ", " . map pretty $ fs + _ -> pretty xs + SumP xs -> case patList xs of + Nothing -> pretty xs + Just cs -> case traverse prettyConstr cs of + Nothing -> pretty cs + Just cstrs -> nest 2 . sumFmt $ cstrs + plist@(p1 :* p2) -> case patList plist of + Just pl -> list . map pretty $ pl + Nothing -> pretty p1 <+> ":*" <+> pretty p2 + Nil -> "Nil" + RefP mn@(ModuleName _) n@(Name _) -> pretty mn <> "." <> pretty n + RefP Nil (Name n) -> pretty n + RefP p1 p2 -> parens $ "Ref" <+> pretty p1 <+> pretty p2 + VarP t -> pretty t + ap@(AppP p1 p2) -> case prettyApp ap of + Just pap -> parens pap + Nothing -> "App" <+> pretty p1 <+> pretty p2 + p1 := p2 -> pretty p1 <+> ":=" <+> pretty p2 + DecP nm args body -> case nm of + Name n -> case patList args of + Nothing -> "Dec" <+> pretty n <+> pretty args <+> "=" <+> pretty body + Just [] -> + "data" + <+> pretty n + <+> "=" + <+> pretty body + Just vars -> + "data" + <+> pretty n + <+> hcat (punctuate " " . map pretty $ vars) + <+> "=" + <+> pretty body + _ -> "Dec" <+> pretty nm <+> pretty args <+> "=" <+> pretty body + where + prettyField :: forall a. Pat -> Maybe (Doc a) + prettyField = \case + Name l := t -> Just $ pretty l <+> "::" <+> pretty t + _ -> Nothing + + prettyConstr :: forall a. Pat -> Maybe (Doc a) + prettyConstr = \case + Name l := (ProdP Nil) -> Just $ pretty l + Name l := t -> Just $ pretty l <+> pretty t + _ -> Nothing + + -- this is kind of annoying to get right, don't think this is it + prettyApp :: forall a. Pat -> Maybe (Doc a) + prettyApp = \case + AppP p1 p2 -> (pretty p1 <+>) <$> prettyApp p2 + other -> Just $ pretty other + +sumFmt :: [Doc a] -> Doc a +sumFmt = encloseSep "" "" " | " + +spaced :: Doc a -> Doc a +spaced d = line <> d <> line + +() :: Doc a -> Doc a -> Doc a +d1 d2 = d1 <> line <> d2 + +() :: Doc a -> Doc a -> Doc a +d1 d2 = d1 <> line <> line <> d2 + +pointies :: Doc a -> Doc a +pointies d = "<<" <> d <> ">>" diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Rules.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Rules.hs new file mode 100644 index 00000000..e262fee4 --- /dev/null +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClass/Rules.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} + +module LambdaBuffers.Compiler.TypeClass.Rules ( + Class (..), + Constraint (..), + Rule (..), + type Instance, + mapPat, +) where + +import LambdaBuffers.Compiler.ProtoCompat qualified as P +import LambdaBuffers.Compiler.TypeClass.Pat (Pat) + +data Class = Class + { name :: P.TyClassRef + , supers :: [Class] + } + deriving stock (Show, Eq, Ord) + +{- A type which represents instances. Can be either a single simple instance or + a complex instance with its instance constraints. We can use the instance constraint + constr (:<=) to write deriving rules using PatVars in the Pat argument. + NOTE: Rule constraints are written backwards, i.e. "purescript-style" + NOTE: All variables to the right of the first :<= must occur to the left of the first :<= +-} + +data Constraint = C Class Pat + deriving stock (Show, Eq, Ord) + +data Rule where + (:<=) :: Constraint -> [Constraint] -> Rule + deriving stock (Show, Eq, Ord) +infixl 7 :<= + +type Instance = Rule + +{- Map over the Pats inside of an Rule +-} +mapPat :: (Pat -> Pat) -> Rule -> Rule +mapPat f (C c ty :<= is) = C c (f ty) :<= map (\(C cx p) -> C cx (f p)) is diff --git a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClassCheck.hs b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClassCheck.hs index 96176167..cb7f5be6 100644 --- a/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClassCheck.hs +++ b/lambda-buffers-compiler/src/LambdaBuffers/Compiler/TypeClassCheck.hs @@ -3,6 +3,7 @@ module LambdaBuffers.Compiler.TypeClassCheck (detectSuperclassCycles, detectSuperclassCycles') where +import Control.Lens.Combinators (view) import Control.Lens.Operators ((^.)) import Data.Generics.Labels () import Data.List (foldl') @@ -10,6 +11,9 @@ import Data.Map qualified as M import Data.Text (Text) import LambdaBuffers.Compiler.ProtoCompat.Types ( ClassDef (), + ForeignClassRef (ForeignClassRef), + LocalClassRef (LocalClassRef), + TyClassRef (ForeignCI, LocalCI), ) import Prettyprinter ( Doc, @@ -30,7 +34,11 @@ detectSuperclassCycles' = detectCycles . mkClassGraph . map defToClassInfo defToClassInfo :: ClassDef -> ClassInfo defToClassInfo cd = ClassInfo (cd ^. #className . #name) $ - map (\x -> x ^. #className . #name) (cd ^. #supers) + map (extractName . view #classRef) (cd ^. #supers) + where + extractName = \case + LocalCI (LocalClassRef nm _) -> nm ^. #name + ForeignCI (ForeignClassRef nm _ _) -> nm ^. #name mkClassGraph :: [ClassInfo] -> M.Map Text [Text] mkClassGraph = foldl' (\acc (ClassInfo nm sups) -> M.insert nm sups acc) M.empty diff --git a/lambda-buffers-compiler/test/Test/Samples/Proto/Module.hs b/lambda-buffers-compiler/test/Test/Samples/Proto/Module.hs index dfec8562..c533ac02 100644 --- a/lambda-buffers-compiler/test/Test/Samples/Proto/Module.hs +++ b/lambda-buffers-compiler/test/Test/Samples/Proto/Module.hs @@ -17,6 +17,7 @@ module'maybe = , P.classDefs = mempty , P.instances = mempty , P.sourceInfo = sourceInfo'empty + , P.imports = mempty } {- | 1 Module containing diff --git a/lambda-buffers-compiler/test/Test/TypeClassCheck.hs b/lambda-buffers-compiler/test/Test/TypeClassCheck.hs index 617b0be2..6363deef 100644 --- a/lambda-buffers-compiler/test/Test/TypeClassCheck.hs +++ b/lambda-buffers-compiler/test/Test/TypeClassCheck.hs @@ -9,7 +9,7 @@ import LambdaBuffers.Compiler.ProtoCompat (IsMessage (fromProto)) import LambdaBuffers.Compiler.ProtoCompat.Types qualified as ProtoCompat import LambdaBuffers.Compiler.TypeClassCheck (detectSuperclassCycles') import Proto.Compiler (ClassDef, Constraint, Kind, Kind'KindRef (Kind'KIND_REF_TYPE)) -import Proto.Compiler_Fields (argKind, argName, arguments, classArgs, className, kindRef, name, supers, tyVar, varName) +import Proto.Compiler_Fields (argKind, argName, arguments, classArgs, className, classRef, kindRef, localClassRef, name, supers, tyVar, varName) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (assertFailure, testCase, (@?=)) @@ -53,7 +53,7 @@ mkclass nm sups = constraint :: Text -> Constraint constraint nm = defMessage - & className . name .~ nm + & classRef . localClassRef . className . name .~ nm & arguments .~ [defMessage & tyVar . varName . name .~ "a"] cycles :: [ClassDef] diff --git a/lambda-buffers-proto/compiler.proto b/lambda-buffers-proto/compiler.proto index 0c478da3..98d7d31b 100644 --- a/lambda-buffers-proto/compiler.proto +++ b/lambda-buffers-proto/compiler.proto @@ -437,13 +437,45 @@ message ClassDef { SourceInfo source_info = 5; } +/* Type class references + +It is necessary to know whether a type class is defined locally or in a +foreign module when referring to it in an instance clause or constraint, +this allows users (and requires the frontend) to explicitly communicate +that information. +*/ + +message TyClassRef { + // Local type reference. + message Local { + // Local module class name. + ClassName class_name = 1; + // Source information. + SourceInfo source_info = 2; + } + // Foreign class reference. + message Foreign { + // Foreign module class name. + ClassName class_name = 1; + // Foreign module name. + ModuleName module_name = 2; + // Source information. + SourceInfo source_info = 3; + } + // Local or a foreign type class reference. + oneof class_ref { + Local local_class_ref = 1; + Foreign foreign_class_ref = 2; + } +} + /* Type class instances Instance clauses enable users to specify 'semantic' rules for their types. */ message InstanceClause { // Type class name. - ClassName class_name = 1; + TyClassRef class_ref = 1; // Head of the instance clause. Currently, the Compiler only accepts single // parameter type classes. repeated Ty heads = 2; @@ -456,7 +488,7 @@ message InstanceClause { /* Constraint expression */ message Constraint { // Name of the type class. - ClassName class_name = 1; + TyClassRef class_ref = 1; // Constraint arguments. repeated Ty arguments = 2; // Source information. @@ -476,8 +508,10 @@ message Module { repeated ClassDef class_defs = 3; // Type class instance clauses. repeated InstanceClause instances = 4; + // Imported modules the Compiler consults when searching for instance clauses. + repeated ModuleName imports = 5; // Source information. - SourceInfo source_info = 5; + SourceInfo source_info = 6; } /* Compiler Input