Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General-purpose AST reflection #212

Closed
krame505 opened this issue Dec 15, 2017 · 33 comments
Closed

General-purpose AST reflection #212

krame505 opened this issue Dec 15, 2017 · 33 comments

Comments

@krame505
Copy link
Member

krame505 commented Dec 15, 2017

Overview

I am proposing that we add a general-purpouse AST reflection library to Silver. This would be useful for implementing embedded host-language AST literals (#194), and may find other uses as well.

Essentially, I am proposing something sort of like hackUnparse, except that it outputs a generic tree structure instead of a string.

closed nonterminal AST with pp;

abstract production nonterminalAST
top::AST ::= prodName::String children::ASTs annotations::NamedASTs
{}

abstract production listAST
top::AST ::= vals::ASTs
{}

abstract production stringAST
top::AST ::= s::String
{}

-- Etc...

nonterminal ASTs with pp;
nonterminal NamedASTs with pp;

-- All the expected cons and nil productions here

nonterminal NamedAST with pp;

abstract production namedAST
top::NamedAST ::= n::String v::AST
{}

It would also be possible to designate certain types occuring in the tree that should not be reflected, but instead placed directly in the result tree.

nonterminal ASTProd;
abstract production astProd
top::ASTProd ::= prod::(AST ::= a)
{}

-- Foreign function
function reflectAST
AST ::= escapes::[Pair<String ASTProd>] value::a
{}

escapes here is a list of pairs of the names of types to escape from reflection and wrapping bridge productions for them. Note that the ASTProd nonterminal is effectively like a newtype in Haskell, since the productions in escapes vary in type.

Example

grammar example:abstractsyntax;

nonterminal Expr with ...;

abstract production fooExpr
top::Expr ::= a::Expr b::Foo c::Decorated Bar
{}

abstract production bazExpr
top::Expr ::= prod::(Expr ::= Expr)
{}

nonterminal Foo with ...;

nonterminal Bar with ...;

-- A bunch more productions here

abstract production fooAST
top::AST ::= f::Foo
{}

abstract production decBarAST
top::AST ::= b::Decorated Bar
{}

abstract production unaryProdAST
top::AST ::= prod::(Expr ::= Expr)
{}

-- A production body somewhere
local ast::Expr = ...;
local reflectedAST::AST =
  reflectAST(
    [pair("example:abstractsyntax:Foo", astProd(fooAST)),
     pair("Decorated example:abstractsyntax:Bar", astProd(decBarAST)),
     pair("(example:abstractsyntax:Expr ::= example:abstractsyntax:Expr)", astProd(unaryProdAST)],
    ast);

Implementation

The implementation is essentially straightforward: a map is built of all the escaped types and their bridge productions. A recursive foreign function somewhat similar to hackUnparse will then transverse the tree, first checking if the object has a type name in the map and if so applying the associated production, or otherwise recursively transforming any children and applying the appropriate default production.

A few things to resolve here:

  • We need some way of getting the name of any type as a string. I suggest that we add an interface TypeNamed to the runtime with a method String getTypeName(), implemented by Node, DecoratedNode, NodeFactory, ConsCell, StringCatter, IOToken, etc. The generated translation for a nonterminal would provide an implementation for this method. Foreign types could implement this interface as well, or otherwise an error would be raised on encountering a foreign type not implementing the interface.
  • For reflecting the values of annotations, we need some general way of finding and accessing all annotations for a nonterminal in a generalized way. I suggest that we add abstract methods String[] getAnnotationNames() and Object getAnnotation(final String name) to the Node class, which would have implementations generated in each nonterminal class.
  • I'm not sure how to best handle type-parametric nonterminals. One option I guess would be to leave the implementation of getTypeName to the generated production classes, and generate code to infer the type parameters from the types of the children. Initially though we can probably just not support escaping type-parametric nonterminals.

@tedinski @ericvanwyk Thoughts on this? I'll probably start working on implementing this in the next week or so if there aren't any major objections.

@ericvanwyk
Copy link
Contributor

ericvanwyk commented Dec 16, 2017 via email

@ericvanwyk
Copy link
Contributor

Hi Lucas,

I'm a bit confused by this. It isn't clear why we need a new AST type.

As I understand things, we want to run some version of an ableC parser on a string, giving us a tree with some ableC type. Perhaps while(..., ....) as an ableC:Stmt typed value. But what we need is a Silver tree: applicationExpr(baseExpr(qNameId(nameIdLower(terminal(IdLower_t("while"))))) , ... ).

This is the Silver tree that silver would generate when parsing "while(..., ...)" if it was written in a forwarding clause for some ableC extension perhaps.

Or are you doing something entirely different? I don't see how the AST gets used.

-Eric

@krame505
Copy link
Member Author

The basic issue is how we would convert from an ableC tree to a Silver tree. I'm not sure what you mean by "run some version of an ableC parser on a string" - the idea is that the ableC grammar would be embedded directly in the Silver grammar, as detailed in #194. There will then be some concrete production in the Silver tree that has as a child the ableC concrete syntax, which is then transformed into an ableC AST of some type (Decl, or Stmt, or whatever.)
Once the Silver production has an ableC AST, it needs to somehow translate it into the Silver tree that it can use to forward, as you mentioned. I outlined a few ways of doing this in #194 - defining a translation attribute on all of ableC (which would be a pain to maintain), kluging things together with hackUnparse and abuse of parsers, or doing something with the Silver runtime.
As I said there, this last option really seems to be the only viable one, and it wouldn't be very hard to make more general-purpose, for example if we wanted to do the same thing in a different host language, or maybe as a part of some sort of debugger. Basicly, this reflection library would just output the general AST type instead of trying to build the corresponding Silver AST directly - it would be easy to write a attribute on AST computing the translation to a Silver Expr, to use as a forward.

@ericvanwyk
Copy link
Contributor

By "run some version of an ableC parser" I meant what you've said in your first paragraph above.

What was missing (or what I missed) was the bit in the last paragraph of converting this AST tree into a Silver Expr tree.

I don't understand your reflectedAST equation above. You've left out the second argument to reflectAST, but maybe that doesn't matter. Why are Foo and Bar not "reflected"? Are these similar to bridge productions from ableC into Silver?

Also, if Decorated Bar is not reflected, then why do you need to say anything about the unary production in bazExpr? If the Decorated Bar is not reflected do you still keep working down that tree?

Maybe a less contrived example would help.

Also, how much of this can be generated from a nice Silver extensions? What you had in #194 for the Decls example is not so bad to read. But I don't see how that connects to this.

Thanks.

@krame505
Copy link
Member Author

krame505 commented Dec 18, 2017

I don't understand your reflectedAST equation above. You've left out the second argument to reflectAST, but maybe that doesn't matter.

Yeah, that's a typo. I meant to pass in ast as the second argument.

Why are Foo and Bar not "reflected"? Are these similar to bridge productions from ableC into Silver?

Yes. The point of this example was just trying to show of some of the various proposed features.

Also, if Decorated Bar is not reflected, then why do you need to say anything about the unary production in bazExpr? If the Decorated Bar is not reflected do you still keep working down that tree?

References don't typically make up a structural part of an AST that we would want to handle recursively - they can point to anything, and can even create cycles. Note that the main ableC AST doesn't contain references, they are only present in 'auxiliary' trees such as Type or in the environment representation. I could imagine there being a reason to perform reflection on a Type tree, but in that case references back to the main AST would exist as 'leaves' in the reflected tree, and wouldn't be something we would want to transverse.

Functions, references, terminals, etc. are all types that can only exist as a 'leaf' in the reflected AST. Since they all have unique types, some sort of new production on the AST nonterminal is needed to represent them, and must be provided in the list of 'escaped' types. If no entry exists this map for an encountered type, then if it is a list, nonterminal, or built-in type we transverse it as such. If a type lacks an entry and there is no default way of handling it, then a runtime error would be raised.

Maybe a less contrived example would help.

Sure. For example in the ableC extension to Silver, we would have something like

import edu:umn:cs:melt:ableC:concretesyntax as cnc;
import edu:umn:cs:melt:ableC:abstractsyntax as abs;

-- ableC concrete syntax bridge production
-- There would be lots of these, corresponding to everything implemented by the substitution library right now.  
concrete production silverExpr_c
top::cnc:Expr_c ::= '$Expr' '{' e::Expr '}'
{
  top.ast = silverExpr(e);
}

-- corresponding ableC abstract syntax bridge production
abstract production silverExpr
top::abs:Expr ::= e::Expr
{
  forwards to error("This production has no forward and should never be decorated!");
}

-- New AST bridge production
abstract production silverExprAST
top::AST ::= e::Expr
{
  top.silverTrans = e;
}

-- Silver extension bridge production
concrete production ableCDeclExpr
top::Expr ::= 'ableC' 'Decl' '{' cst::cnc:Declaration_c '}'
{
  local ast::abs:Decl = cst.ast;
  local reflectedAST::AST =
    reflectAST(
      [pair("silver:definition:core:Expr", astProd(silverExprAST))],
      ast);
  forwards to reflectedAST.silverTrans;
}

-- AST translation to Silver
synthesized attribute silverTrans<a>::a;
attribute silverTrans<Expr> occurs on AST;
attribute silverTrans<AppExprs> occurs on ASTs;
attribute silverTrans<AnnoAppExprs> occurs on NamedASTs;

-- Needed since AST is a closed nonterminal
aspect default production
top::AST ::=
{
  top.silverTrans = error("AST does not have silverTrans defined");
}

aspect production nonterminalAST
top::AST ::= prodName::String children::ASTs annotations::NamedASTs
{
  top.silverTrans =
    case prodName, children of
      "silverExpr", consAST(h, t) -> h.silverTrans
    | _, _ -> applicationExpr(baseExpr(qNameId(nameIdLower(terminal(IdLower_t(prodName))))) , children.silverTrans, annotations.silverTrans)
    end;
}

aspect production stringAST
top::AST ::= s::String
{
  top.silverTrans = stringConst(terminal(String_t, s));
}

-- etc...

Make sense?

Also, how much of this can be generated from a nice Silver extensions? What you had in #194 for the Decls example is not so bad to read. But I don't see how that connects to this.

Not sure I understand the question. But hopefully the example helped?

@ericvanwyk
Copy link
Contributor

OK, much better! Without an example it was difficult to even see what your intentions were.

This makes much more sense. I thought much of the other Silver code was to be written as part of an ableC extension, not the extension to Silver to support ableC.

Minor comment - do we want a default production for silverTrans on AST? Your error message is less helpful than the one that Silver would give.

What if someone is building an extension that extends some other extension? How easy would it be to create a new version of Silver for ableC + some-random-extension?

We certainly need to do some work on Silver to make creating multiple domain-specific instances of Silver more feasible. The "silver" script we have now is not so useful. Silver should build in a way much more like ableC does.

@krame505
Copy link
Member Author

Minor comment - do we want a default production for silverTrans on AST? Your error message is less helpful than the one that Silver would give.

Since AST is a closed nonterminal, it is needed to avoid errors in the MWDA.

What if someone is building an extension that extends some other extension? How easy would it be to create a new version of Silver for ableC + some-random-extension?

Basically there are 3 components I am thinking of adding here:

  • This general-purpose reflection library
  • A general silver extension library for adding embedded host language literals to Silver, not just for ableC (containing the definition and aspect productions for silverTrans on AST)
  • The Silver extension containing the bridge production to ableC (and any concrete syntax added to ableC)

Adding more extensions would be as simple as adding them to the top-level Silver parser, I think, since an extension to ableC would now be effectively also be an extension to Silver, which has become a host language containing ableC as a component.

@ericvanwyk
Copy link
Contributor

OK, I think this sounds like it is worth trying.

@tedinski - last chance to object before we start on this. Any comments?

@tedinski
Copy link
Member

I have thoughts about things related to the concrete syntax embedding, but maybe that can wait, since many of them would mostly amount to tweaks that would ensure a smooth path between "stuffing the ablec grammar into silver" as just a first prototype method on the way to something better.

I like the idea of reflecting trees as a tool here. This would let us deprecate hackUnparse and I believe even allow us to eliminate env_parser in the Silver compiler, which would be a VERY good thing! Basically, are we envisioning a reflect/reify pair of operations that turns Silver trees into AST and back? Along with a parse/unparse operation that would turn AST trees into strings and back? I'd love to try to see just a first implementation that does that (in real working code) before we start trying to get fancier with the next step of adapting it to make sure it does what you need for concrete embedding stuff.

I do lose the thread in that stuff, though.

  1. I don't think AST should be closed.
  2. I don't understand the purpose of ASTProd.
  3. Instead of the present approach of writing an AST -> silver:Expr function (or attribute), consider instead writing an AST -> AST function, which we then reify into a silver:Expr.

i.e. We'd do this for syntax embedding:

  1. Used the (modified) AbleC parser to get a concrete syntax tree.
  2. Transform to abstract syntax.
  3. Reflect to AST
  4. Rewrite AST to, instead of correspond to AbleC AST nodes, correspond to Silver AST nodes that construct the object-level AbleC AST nodes.
  5. Reify that tree back as a Silver Expr.

This avoids the need for new silverExprAST productions, allowing things to be non-closed and simpler.

I think, possibly, you hadn't planned on reify being a thing. But I think this is both important as a generic operation on these kinds of reflection trees, and also the thing we'd need to eliminate env_parser from the Silver compiler. Implementing reify might be a bit tricky though. We'd probably want to do some kind of type checking to ensure the resulting tree is sensible... Not entirely sure how we should go about that, especially given parameterized types. This might actually be the trickiest part of getting this right.

@tedinski
Copy link
Member

Actually, implementing reify might not be quite as tricky as I thought. It'll have to be a new production in the Silver AST, and not an ordinary function, but it should be simple enough, and we can make it behave like an ordinary function... we can discuss this later though.

@krame505
Copy link
Member Author

I hadn't given much thought to reify, but this does sound important to have. This would be a much better approach that what I suggested for 'escaping' sub-trees.
What I am wondering, though, is how would this work for trees containing e.g. references or functions? Presumably we just wouldn't be able to parse these trees (and unparse would obviously loose some information.) Not sure how this would work with respect to reify.
ASTProd is supposed to be a generic wrapper around an AST production of any type. The reason I included (and had AST be closed) this is that I thought that we would need new AST productions for every new type of reference, function, etc.
Also, how would this work with foreign types? Presumably we would still just raise an error on encountering these?

@tedinski
Copy link
Member

how would this work for trees containing e.g. references or functions?

reflect would probably be fine understanding a handleful of types and then shoving the rest into a foreignAST :: (AST ::= a) production that can hold anything.

unparse would fail on trees containing a foreignAST, and we should probably design it with that in mind (i.e. returns an unparse or an error message.) (Plus, maybe we should have a debugUnparse that does what hackUnparse does right now and just stuffs Java's Object.toString in there.)

@ericvanwyk
Copy link
Contributor

@tedinski - about the AST -> AST function your first point number 3 above. Is this what turns reflected ableC trees into reflected Silver trees? Then reify gives us the Silver tree we want to forward to, at least for this embedding business? If so, what happens when reify is applied to a reflected tree that isn't a Silver tree? Might we ever want to reify an ableC tree?

@krame505
Copy link
Member Author

I'm not positive this is exactly what Ted is thinking, but the way I understand it is this: Once we have an ableC tree (a Stmt/Expr/whatever), we would pass that to reflect to get an AST tree structure. We would define an attribute on AST that computes a translation to a Silver Expr. This translation on nonterminalAST would first check the production name, and if it is an ableC-to-Silver bridge production, then reify the contained AST representation of Silver code back into a Silver Expr, which would be the translation on that AST node. Otherwise, return the generic Silver expression that applies a production with that name to the translations of the parameters.

reflect/reify should be general enough to work for any type of AST, and ableC extensions might find a use for it in some kinds of EDSLs. But for embedding Silver code in ableC we would only ever be reifying into Silver Exprs.

What I still don't understand, though, is would reify simply be a foreign function similar to reflect? Or would it need to be baked into Silver in a deeper way? It seems like adding reify would require opening a hole in the Silver type system without some special treatment.

@tedinski
Copy link
Member

@ericvanwyk basically, reify(reflect(x)) == x for any x.

@krame505

We would define an attribute on AST that computes a translation to a Silver Expr.

...to an AST representing a Silver Expr, which we'd later reify the whole thing of to be "real". Otherwise, I think you've got it?

What I still don't understand, though, is would reify simply be a foreign function similar to reflect?

No, but only because we need special type information. Basically, we'd need to know the exact output type of reify to invoke it, which since we don't have type classes, we can't do that "within" our type system. So we'd need a special form (a new production) that runs type checking, looks at the final type, and yields as translation a call to the reify funtions in the runtime, giving the type (with any parameters) expected as the result. The reify function would then have to do a little runtime type checking, but this isn't such a bad thing.

We'd probably want to make this behave identically to a function, so people can map it and whatnot.

@krame505
Copy link
Member Author

We would define an attribute on AST that computes a translation to a Silver Expr.

...to an AST representing a Silver Expr, which we'd later reify the whole thing of to be "real". Otherwise, I think you've got it?

I guess what I was envisioning was translating the overall tree directly to the Silver Expr doing the production application, and just calling reify when we encounter an ableC-to-Silver bridge production. Expressing the production applications in terms of AST seems less clean I guess? Not a big deal I guess, we can chat tomorrow.

So we'd need a special form (a new production) that runs type checking, looks at the final type, and yields as translation a call to the reify functions in the runtime, giving the type (with any parameters) expected as the result. The reify function would then have to do a little runtime type checking, but this isn't such a bad thing.

Ah, a new production in the Silver compiler, not the reflection library - sort of like print or whatever? Makes sense.

@krame505
Copy link
Member Author

krame505 commented Jan 5, 2018

@tedinski What are your thoughts on the dynamic type-checking for reification of foreignAST? Obviously this isn't possible for actual foreign types (for which some sort of casting error would eventually be raised somewhere else), but what about (potentially type-parametric) references?

@krame505
Copy link
Member Author

@tedinski I've started to work on implementing the reify function literal, but I'm having trouble figuring out how to do the translation for this. Essentially, I need the inferred return Type of the function literal, but I'm not familiar enough with how type inference in Silver works to access this.

@krame505
Copy link
Member Author

krame505 commented Jan 11, 2018

Another issue that I have run across is in attempting to reify productions with type variables not bound on the LHS of the signature. For example, in reifying production foo :: (Expr ::= a), what TypeRep object is passed down recursively?
To solve this, as well as the above-mentioned problem with dynamic type checking of foreignAST, it seems that we need a way of dynamically extracting a type representation from any object at runtime. This can be done by creating an interface Typed containing a function returning the TypeRep of an object, implemented by every runtime value class (Node, DecoratedNode, NodeFactory, etc.) as well as potentially by foreign types.
The TypeRep class will also need to be changed into something more flexible and capable of representing function and skolem types. Basic equality checking between types is then replaced by a unification process, which instead of returning a substitution, would actually update any skolem type objects to point to the actual type.

@tedinski
Copy link
Member

What are your thoughts on the dynamic type-checking for reification of foreignAST?

You are finding all the fun corner cases!

Let me just see if I understand correctly? The trouble is that we get as far as having to reify(ForeignTypeRep("x", [...]), foreignAST(y))? We can now check if the object y has type "x" using Java reflection, but we're at a loss about what to do with the type parameters?

Good question. Any ideas? I does sound to me like maybe this is something that foreign types might have to provide somehow...

One thought: we could modify foreignAST to contain its TypeRep.

Essentially, I need the inferred return Type of the function literal, but I'm not familiar enough with how type inference in Silver works to access this.

There's a function called finalType that should be instructive, let me know if it needs more explanation.

Another issue that I have run across is in attempting to reify productions with type variables not bound on the LHS of the signature. ... what TypeRep object is passed down recursively?

Ah, for this we'd need to accept any type, I suppose. But you're right there's a problem! We need to make sure those types are the same where they should be the same. e.g.

Boolean ::= a a

Oh my, this is getting complicated.

it seems that we need a way of dynamically extracting a type representation from any object at runtime. This can be done by...

This is a good idea, but it's also biting off a big chunk right away.

Can we break this into pieces? What if we error out on any foreign type with a type parameter, and any production with existential variables (type variables in the children that aren't in the LHS). This would allow us to implement something that sometimes/usually works, and then get to figuring out how to handle these new issues, right? (And maybe discover any other new issues along the way?)

I do think we should handle these before we decide this feature is done, but I just think we should get something to working without its scope blowing up too big right away. :)

The TypeRep class will also need to be changed into something more flexible and capable of representing function and skolem types.

Yeah, it does seem to be blowing up...

@krame505
Copy link
Member Author

Let me just see if I understand correctly? The trouble is that we get as far as having to reify(ForeignTypeRep("x", [...]), foreignAST(y))? We can now check if the object y has type "x" using Java reflection, but we're at a loss about what to do with the type parameters?

Yeah, but not just for foreign types. Remember, we are shoving anything that isn't a nonterminal, list, or builtin type into foreignAST, so this also needs to wrap references, terminals, functions, etc. (maybe foreignAST isn't the greatest name anymore...)

But yes, for foreign types this would need to be explicitly specified, somehow. We can probably just raise a runtime error when reifying any foreign type that doesn't have a TypeRep specified (x instanceof Typed if we do this as implementing a Typed interface.)

One thought: we could modify foreignAST to contain its TypeRep.

I thought about doing this instead, but it turns out to be pretty complicated in practice. Also it doesn't stop somebody from constructing a foreignAST manually with the wrong TypeRep and calling reify.

Can we break this into pieces? What if we error out on any foreign type with a type parameter, and any production with existential variables (type variables in the children that aren't in the LHS). This would allow us to implement something that sometimes/usually works, and then get to figuring out how to handle these new issues, right? (And maybe discover any other new issues along the way?)

That is generally my plan, but I am changing the implementation of TypeRep now to avoid more work later on. It does seem to be shaping up fairly nicely so far.

@krame505
Copy link
Member Author

Just a heads-up...
It turns out that we really do need to have the type of any object to be reified accessible at runtime (e.g. when figuring out the type parameters to a nonterminal wrapped in anyAST.) This means that I'll need to subclass Integer, Boolean and Float in order to implement the Typed interface for these. Are there any potential issues to worry about here?
On a more positive note, basically everything else seems to be working with reify (at least what I've been able to test so far.)

@tedinski
Copy link
Member

Eh, subclassing those is not a good approach. Can't those be special cased with instanceof or something?

@krame505
Copy link
Member Author

krame505 commented Jan 17, 2018 via email

@krame505
Copy link
Member Author

Does Silver really not have a way of writing type expressions for functions with named parameters?

@krame505
Copy link
Member Author

Also another question: Can partial application of something with named parameters ever yield something else with named parameters, or do all non-provided named params need to be converted to ordered?
@tedinski

@tedinski
Copy link
Member

Does Silver really not have a way of writing type expressions for functions with named parameters?

Hah, yeah. Annotations never really got completed as a feature beyond what was necessary to make location work okay.

Can partial application of something with named parameters ever yield something else with named parameters, or do all non-provided named params need to be converted to ordered?

Honestly, I forget the state of this implementation. I believe the answer is: we WANT that to be possible, but (a) the lack of type expressions and (b) the need to invent appropriate syntax means that it's not currently possible. But maybe the runtime object for partial application is capable of supporting it...

@krame505
Copy link
Member Author

OK, thanks. I was a bit confused because the runtime seemed to support this, but I couldn't actually figure out how to test it.

@krame505
Copy link
Member Author

Alright, part 1 (reflection) and part 2 (reification) are now done (pending figuring out why Jenkins is failing...), so on to part 3 (silver:reflect AST utility library.)
As I envision it, this should include at least:

  1. pp on AST that produces a nicely displayable representation of an AST, including anyAST nodes.
  2. unparse on AST that serializes an AST to a string. Mostly the same as pp, but error on anyAST nodes.
  3. A parser that deserializes an unparsed AST from a string.

My question is where all of this should live.

  • The AST nonterminal needs to be in core:reflect since it's a runtime dependency
  • 1 and 2 kinda shouldn't be dependencies of core:reflect, since we want to avoid a dependency on langutil
  • 3 really shouldn't be a dependency of core:reflect since it involves building a parser.

The issue is how to avoid making the unparse and pp equations in silver:reflect be considered orphaned occurs? We can't make silver:reflect an option of core:reflect, since that would still be a build dependency. The only thing that I can think of doing is making silver:reflect an option of silver:langutil... maybe that isn't so bad. Thoughts?

@tedinski
Copy link
Member

Could just not use any of the attributes from langutil.

@krame505
Copy link
Member Author

I mean, I guess I could... but to me it seems not really a good idea to have multiple attributes with the same name in our standard libraries. That would force anybody wanting to import both silver:reflect and silver:langutil to qualify one of them.
I guess it doesn't seem like a bad thing for one standard library to specify that another standard library contains some 'official' occurrences for attributes in the first library (which is what we would be doing by making silver:reflect an option in silver:langutil.) Do you see an issue with this?

@tedinski
Copy link
Member

You can give them different names: serialize deserialize debugAST or whatever

@krame505
Copy link
Member Author

krame505 commented Mar 10, 2018

OK, since #217 with all the reflection library stuff is now merged and building properly, this feature is essentially complete, so I'm closing this issue now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants