Skip to content

Commit 45577f0

Browse files
authored
Merge pull request #36 from mlabs-haskell/compiler/typeclasses-infrastructure
Compiler/typeclasses infrastructure
2 parents b369820 + e4d4d5e commit 45577f0

File tree

10 files changed

+406
-18
lines changed

10 files changed

+406
-18
lines changed

lambda-buffers-compiler/lambda-buffers-compiler.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ library
114114
LambdaBuffers.Compiler.NamingCheck
115115
LambdaBuffers.Compiler.ProtoCompat
116116
LambdaBuffers.Compiler.ProtoCompat.Types
117+
LambdaBuffers.Compiler.TypeClass.Pat
118+
LambdaBuffers.Compiler.TypeClass.Pretty
119+
LambdaBuffers.Compiler.TypeClass.Rules
117120
LambdaBuffers.Compiler.TypeClassCheck
118121

119122
hs-source-dirs: src

lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat.hs

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ throwNamingError = either (Left . NamingError) return
5454

5555
-- TODO(bladyjoker): Revisit and make part of compiler.proto
5656
data ProtoError
57-
= MultipleInstanceHeads ClassName [Ty] SourceInfo
58-
| NoInstanceHead ClassName SourceInfo
59-
| NoConstraintArgs ClassName SourceInfo
60-
| MultipleConstraintArgs ClassName [Ty] SourceInfo
57+
= MultipleInstanceHeads TyClassRef [Ty] SourceInfo
58+
| NoInstanceHead TyClassRef SourceInfo
59+
| NoConstraintArgs TyClassRef SourceInfo
60+
| MultipleConstraintArgs TyClassRef [Ty] SourceInfo
6161
| NoClassArgs ClassName SourceInfo
6262
| MultipleClassArgs ClassName SourceInfo
6363
| NoTyAppArgs SourceInfo
@@ -387,6 +387,41 @@ instance IsMessage P.Product'Record'Field Field where
387387
Classes, instances, constraints
388388
-}
389389

390+
instance IsMessage P.TyClassRef'Local LocalClassRef where
391+
fromProto lr = do
392+
si <- fromProto $ lr ^. P.sourceInfo
393+
nm <- fromProto $ lr ^. P.className
394+
pure $ LocalClassRef nm si
395+
396+
toProto (LocalClassRef nm si) =
397+
defMessage
398+
& P.className .~ toProto nm
399+
& P.sourceInfo .~ toProto si
400+
401+
instance IsMessage P.TyClassRef'Foreign ForeignClassRef where
402+
fromProto fr = do
403+
si <- fromProto $ fr ^. P.sourceInfo
404+
mn <- fromProto $ fr ^. P.moduleName
405+
tn <- fromProto $ fr ^. P.className
406+
pure $ ForeignClassRef tn mn si
407+
408+
toProto (ForeignClassRef tn mn si) =
409+
defMessage
410+
& P.className .~ toProto tn
411+
& P.moduleName .~ toProto mn
412+
& P.sourceInfo .~ toProto si
413+
414+
instance IsMessage P.TyClassRef TyClassRef where
415+
fromProto tr = case tr ^. P.maybe'classRef of
416+
Nothing -> throwProtoError $ OneOfNotSet "class_ref"
417+
Just x -> case x of
418+
P.TyClassRef'LocalClassRef lr -> LocalCI <$> fromProto lr
419+
P.TyClassRef'ForeignClassRef f -> ForeignCI <$> fromProto f
420+
421+
toProto = \case
422+
LocalCI lr -> defMessage & P.localClassRef .~ toProto lr
423+
ForeignCI fr -> defMessage & P.foreignClassRef .~ toProto fr
424+
390425
instance IsMessage P.ClassDef ClassDef where
391426
fromProto cd = do
392427
si <- fromProto $ cd ^. P.sourceInfo
@@ -411,7 +446,7 @@ instance IsMessage P.ClassDef ClassDef where
411446
instance IsMessage P.InstanceClause InstanceClause where
412447
fromProto ic = do
413448
si <- fromProto $ ic ^. P.sourceInfo
414-
cnm <- fromProto $ ic ^. P.className
449+
cnm <- fromProto $ ic ^. P.classRef
415450
csts <- traverse fromProto $ ic ^. P.constraints
416451
hds <- ic ^. (P.heads . traversing fromProto)
417452
hd <- case hds of
@@ -422,15 +457,15 @@ instance IsMessage P.InstanceClause InstanceClause where
422457

423458
toProto (InstanceClause cnm hd csts si) =
424459
defMessage
425-
& P.className .~ toProto cnm
460+
& P.classRef .~ toProto cnm
426461
& P.heads .~ pure (toProto hd)
427462
& P.constraints .~ (toProto <$> csts)
428463
& P.sourceInfo .~ toProto si
429464

430465
instance IsMessage P.Constraint Constraint where
431466
fromProto c = do
432467
si <- fromProto $ c ^. P.sourceInfo
433-
cnm <- fromProto $ c ^. P.className
468+
cnm <- fromProto $ c ^. P.classRef
434469
args <- c ^. (P.arguments . traversing fromProto)
435470
arg <- case args of
436471
[] -> throwProtoError $ NoConstraintArgs cnm si
@@ -440,7 +475,7 @@ instance IsMessage P.Constraint Constraint where
440475

441476
toProto (Constraint cnm arg si) =
442477
defMessage
443-
& P.className .~ toProto cnm
478+
& P.classRef .~ toProto cnm
444479
& P.arguments .~ pure (toProto arg)
445480
& P.sourceInfo .~ toProto si
446481

@@ -454,15 +489,17 @@ instance IsMessage P.Module Module where
454489
tdefs <- traverse fromProto $ m ^. P.typeDefs
455490
cdefs <- traverse fromProto $ m ^. P.classDefs
456491
insts <- traverse fromProto $ m ^. P.instances
492+
impts <- traverse fromProto $ m ^. P.imports
457493
si <- fromProto $ m ^. P.sourceInfo
458-
pure $ Module mnm tdefs cdefs insts si
494+
pure $ Module mnm tdefs cdefs insts impts si
459495

460-
toProto (Module mnm tdefs cdefs insts si) =
496+
toProto (Module mnm tdefs cdefs insts impts si) =
461497
defMessage
462498
& P.moduleName .~ toProto mnm
463499
& P.typeDefs .~ (toProto <$> tdefs)
464500
& P.classDefs .~ (toProto <$> cdefs)
465501
& P.instances .~ (toProto <$> insts)
502+
& P.imports .~ (toProto <$> impts)
466503
& P.sourceInfo .~ toProto si
467504

468505
instance IsMessage P.CompilerInput CompilerInput where

lambda-buffers-compiler/src/LambdaBuffers/Compiler/ProtoCompat/Types.hs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ module LambdaBuffers.Compiler.ProtoCompat.Types (
1414
Field (..),
1515
FieldName (..),
1616
ForeignRef (..),
17+
ForeignClassRef (..),
1718
InstanceClause (..),
1819
Kind (..),
1920
KindRefType (..),
2021
KindCheckError (..),
2122
KindType (..),
2223
LBName (..),
2324
LocalRef (..),
25+
LocalClassRef (..),
2426
Module (..),
2527
ModuleName (..),
2628
ModuleNamePart (..),
@@ -35,6 +37,7 @@ module LambdaBuffers.Compiler.ProtoCompat.Types (
3537
TyApp (..),
3638
TyArg (..),
3739
TyBody (..),
40+
TyClassRef (..),
3841
TyDef (..),
3942
TyName (..),
4043
TyRef (..),
@@ -181,6 +184,21 @@ data Product
181184
| TupleI Tuple
182185
deriving stock (Show, Eq, Ord, Generic)
183186

187+
data ForeignClassRef = ForeignClassRef
188+
{ className :: ClassName
189+
, moduleName :: ModuleName
190+
, sourceInfo :: SourceInfo
191+
}
192+
deriving stock (Show, Eq, Ord, Generic)
193+
194+
data LocalClassRef = LocalClassRef {className :: ClassName, sourceInfo :: SourceInfo}
195+
deriving stock (Show, Eq, Ord, Generic)
196+
197+
data TyClassRef
198+
= LocalCI LocalClassRef
199+
| ForeignCI ForeignClassRef
200+
deriving stock (Show, Eq, Ord, Generic)
201+
184202
data ClassDef = ClassDef
185203
{ className :: ClassName
186204
, classArgs :: TyArg
@@ -191,15 +209,15 @@ data ClassDef = ClassDef
191209
deriving stock (Show, Eq, Ord, Generic)
192210

193211
data InstanceClause = InstanceClause
194-
{ className :: ClassName
212+
{ classRef :: TyClassRef
195213
, head :: Ty
196214
, constraints :: [Constraint]
197215
, sourceInfo :: SourceInfo
198216
}
199217
deriving stock (Show, Eq, Ord, Generic)
200218

201219
data Constraint = Constraint
202-
{ className :: ClassName
220+
{ classRef :: TyClassRef
203221
, argument :: Ty
204222
, sourceInfo :: SourceInfo
205223
}
@@ -210,6 +228,7 @@ data Module = Module
210228
, typeDefs :: [TyDef]
211229
, classDefs :: [ClassDef]
212230
, instances :: [InstanceClause]
231+
, imports :: [ModuleName]
213232
, sourceInfo :: SourceInfo
214233
}
215234
deriving stock (Show, Eq, Ord, Generic)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
3+
module LambdaBuffers.Compiler.TypeClass.Pat (
4+
Pat (..),
5+
toProd,
6+
toRec,
7+
toSum,
8+
patList,
9+
matches,
10+
) where
11+
12+
import Data.Text (Text)
13+
14+
{- A simple ADT to represent patterns.
15+
16+
Note that this ADT allows us to represent nonsensical types (i.e. we can "put the wrong pattern in a hole").
17+
This could be ameliorated by using a GADT, which would give us correct-by-construction patterns at the
18+
cost of significantly more complex type signatures.
19+
-}
20+
21+
data Pat
22+
= {- extremely stupid, unfortunately necessary -}
23+
Name Text
24+
| ModuleName [Text] -- also stupid, also necessary -_-
25+
| Opaque
26+
| {- Lists (constructed from Nil and :*) with bare types are used to
27+
encode products (where a list of length n encodes an n-tuple)
28+
Lists with field labels (l := t) are used to encode records and sum types
29+
These representations let us "peer into the structure" of the TyBody, and are
30+
somewhat analogous to the Generics.SOP representation or, in the case of records (or sums
31+
interpreted as variants), to a row-types representation. We can imagine that each record and
32+
sum are backed by an implicit row.
33+
Unfortunately this encoding allows us to generate Pats which do not correspond to
34+
any possible types. For the purposes of instance resolution/code generation this shouldn't matter
35+
so long as the patterns are only generalizations of "real" types. We could ameliorate this problem by
36+
using a GADT for Pat, but this would greatly complicate the constraint solving/deriving
37+
algorithms and require copious use of type families (and possibly singletons).
38+
-}
39+
Nil -- Nil and :* are hacks to write rules for ProdP and SumP. A bare Nil == Unit
40+
| Pat :* Pat -- cons
41+
| Pat := Pat {- field labels or constr names. The LHS should be (Name "Foo")
42+
for schema types, but should be a PatVar for deriving rules and instances -}
43+
| RecP Pat {- where the Pat arg is expected to be (l := t :* rest) or Nil, where rest
44+
is also a pat-list of labeled fields or Nil -}
45+
| ProdP Pat {- Pat arg should be a list of "Bare types" -}
46+
| SumP Pat {- where the Pat arg is expected to be (Constr l t :* rest) or Nil, where
47+
rest is either Nil or a tyList of Constrs -}
48+
| VarP Text {- This isn't a type variable. Although it is used to represent them in certain contexts,
49+
it is also used more generally to refer to any "hole" in a pattern to which another pattern
50+
may be substituted. We could have separate constr for type variables but it doesn't appear to be
51+
necessary at this time. -}
52+
| RefP Pat Pat {- 1st arg should be a ModuleName -}
53+
| AppP Pat Pat {- Pattern for Type applications -}
54+
| {- This last one is a bit special. This represents a complete type declaration.
55+
The first Pat should be instantiated to `Name l` where l is a concrete name.
56+
The second Pat should be instantiated to a Pat-List (using :*/Nil) which only contains Names.
57+
The final Pat should be instantiated to a Pat body.
58+
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
59+
outermost inner sum for constructing types). -}
60+
DecP Pat Pat Pat
61+
deriving stock (Show, Eq, Ord)
62+
63+
infixr 5 :*
64+
65+
{- Utility functions. Turn a list of types into a product/record/sum type.
66+
-}
67+
toProd :: [Pat] -> Pat
68+
toProd = ProdP . foldr (:*) Nil
69+
70+
toRec :: [Pat] -> Pat
71+
toRec = RecP . foldr (:*) Nil
72+
73+
toSum :: [Pat] -> Pat
74+
toSum = SumP . foldr (:*) Nil
75+
76+
{- Converts a pattern that consists of a well formed pattern list
77+
(i.e. patterns formed from :* and Nil) into a list of patterns.
78+
-}
79+
patList :: Pat -> Maybe [Pat]
80+
patList = \case
81+
Nil -> Just []
82+
p1 :* p2 -> (p1 :) <$> patList p2
83+
_ -> Nothing
84+
85+
{- This is used as a predicate to filter instances or Gens which are structurally compatible
86+
with the argument type.
87+
The first argument is the inner Pat from an instance head or Gen.
88+
The second argument is the Pat representation of a type that we want to derive an instance / generate code for.
89+
NOTE: Is not bidirectional! The first Pat has to be more general than the first
90+
(more specifically: The second Pat should be a substitution instance of the first)
91+
-}
92+
matches :: Pat -> Pat -> Bool
93+
matches t1 t2 | t1 == t2 = True -- need the guard
94+
matches (VarP _) _ = True
95+
matches (x :* xs) (x' :* xs') = matches x x' && matches xs xs'
96+
matches (l := t) (l' := t') = matches l l' && matches t t'
97+
matches (ProdP xs) (ProdP xs') = matches xs xs'
98+
matches (RecP xs) (RecP xs') = matches xs xs'
99+
matches (SumP xs) (SumP xs') = matches xs xs'
100+
matches (AppP t1 t2) (AppP t1' t2') = matches t1 t1' && matches t2 t2'
101+
matches (RefP mn t1) (RefP mn' t2) = matches mn mn' && matches t1 t2
102+
matches (DecP t1 t2 t3) (DecP t1' t2' t3') =
103+
matches t1 t1' && matches t2 t2' && matches t3 t3'
104+
matches _ _ = False

0 commit comments

Comments
 (0)