#599 Thoughts on Symbols

JohnDG Mon 18 May 2009

Here's my sketch of a symbol proposal:

A symbol is anything of the form:

@foo

Symbols are constant -- their values do not change.

The type of a symbol is Symbol, which has, among other methods, a val() method that returns the value of the symbol.

Symbols are bound to the pod in which they are used.

They are two types of symbols:

  1. Bound symbols. These are symbols that are bound to a method, class, etc. The value for such symbols must be specified at compile-time. These symbols are known as facets. Different instances of the same symbol may have different values.
  2. Unbound symbols. These are free standing symbols that are not attached to anything. Their value is typically determined at application startup by localization or configuration files, but it is also possible to specify a default value at compile-time. All instances of the same symbol have the same value.
@boundSymbol="foo"
Void bar() { 
    s1 := @unboundSymbol
}

@boundSymbol="bar"
Void foo() {
    s2 := @unboundSymbol

    // s1 == s2
}

Unbound symbols do not need to be declared in order to be used. They are similar to string literals in languages that support them.

The value of an unbound symbol with no default is determined by the following procedure:

  1. Search for symbol value in default localization file.
  2. Search for symbol value in configuration override.
  3. Search for symbol value in bundled configuration file.

If a value for the symbol cannot be found in any of the above locations, the symbol acquires the value null.

All strings and URIs support symbol interpolation:

s := "Hello, @worldSymbol"

The interpolation is performed on the value of the symbol.

tactics Mon 18 May 2009

This seems pretty clean and straightforward.

tompalmer Mon 18 May 2009

Symbols are bound to the pod in which they are used.

Maybe I misunderstand, but I like Brian's recommendation which allows symbols from other pods to be used.

JohnDG Mon 18 May 2009

OtherPod::@mySymbol, using OtherPod::@mySymbol, etc.

tactics Mon 18 May 2009

Are you thinking that symbols should be declared in the pod build script? Or did you have another idea in mind?

brian Mon 18 May 2009

John, thanks for your feedback. You are going down the same path I was trying to go down last week - unification of facets and symbols. I like your idea of "bound" and "unbound".

But here is the problem I ran against which I think we have in your bound/unbound proposal: the notion of declaration versus usage. Let's consider the transient facet used to mark a field as not-serialized:

** Declaration of transient for fandoc and default value
Bool transient := false

The declaration is important for a bunch of reasons - most importantly because it creates a qualified symbol name @sys::transient. We can reflect the symbol and document it too.

But declaration is different than usage:

@transient Str someField

// remember that is just sugar for this:
@transient=true Str someField

In this case, the declaration @sys::transient must already exist so that my using statements can resolve it. Not only do I need @transient declared for namespace resolution, but I want to generate a compiler error if it hasn't been declared. Static type checking was a key benefit of the original proposal.

So there is definitely a difference between declaration versus usage. So I am not sure how facet usage can also be declaration - we really need two different constructs (say the difference between := and =).

tompalmer Mon 18 May 2009

So there is definitely a difference between declaration versus usage. So I am not sure how facet usage can also be declaration - we really need two different constructs (say the difference between := and =).

I'm trying hard to understand, but I don't get the issue.

Side note, wouldn't you want to declare @transient like so for a default to true?

Bool transient := true

Alternatively, you could make "presence-only" facets like so:

Void? transient := null

Then, either it is there (and null) or it isn't, but slot.facets.containsKey(@transient) needs so much more typing than slot.facet(@transient). Does null evaluate to false in boolean tests, by the way? Sorry not in a convenient circumstance to try it out right now.

brian Tue 19 May 2009

Side note, wouldn't you want to declare @transient like so for a default to true?

A field is considered persistent unless explicitly marked transient, so the default is false. I don't want to get hung up on boolean facets, because they are sort of a special case in that @foo is sugar for @foo=true.

The issue is that within the same pod, I need to be able to distinguish between a declaration and a usage in to do namespace binding and compile-time checking.

JohnDG Tue 19 May 2009

The issue is that within the same pod, I need to be able to distinguish between a declaration and a usage in to do namespace binding and compile-time checking.

How about requiring that bound symbols be declared beforehand? e.g.:

class MyFacets {
    public static Symbol @serializable
    public static Symbol @transient
}

Or something of that nature. Making facets ordinary instance variables would also alleviate the namespace concerns.

brian Tue 19 May 2009

Or something of that nature. Making facets ordinary instance variables would also alleviate the namespace concerns.

But really they are "fields" on pod. It is @sys::transient, not @sys::MyFacets.transient. If we go back to my original proposal, that is effectively what we are doing, declaring symbols at the pod level. Then using them as keys for facets - but clearly symbols and facets are two different things in that proposal.

tompalmer Tue 19 May 2009

@foo is sugar for @foo=true.

Thanks for clarifying/reminding. That makes sense. So the discussion of "default value" means that saying slot.facet(@mySymbol) would return that default value if the facet is missing rather than null?

If so, I guess slot.facet(@mySymbol, null) could still give null no matter what. If that's what a particular person wanted. And slot.facets.containsKey(@mySymbol) would still be available for those needing an explicit answer, too, even though it seems somewhat like bad form to dig so deep, now that I (hope I) understand what is meant by the default value.

The issue is that within the same pod, I need to be able to distinguish between a declaration and a usage

The examples you've shown don't look ambiguous to me. I'm just missing something, but if everyone else gets it, I guess I'll just be the odd one out for now.

tompalmer Tue 19 May 2009

And I see that is indeed what you recommended previously:

Obj? facet(Symbol sym, Obj? def := sym.val)

My bad missing that before.

brian Tue 19 May 2009

Based on this conversation, I'm still thinking my original proposal is best:

  • symbols are identifiers added to the pod namespace
  • @symbol is a symbol literal use name resolution just like types
  • facets are keyed by a symbol

But I might let this feature bake a bit longer before diving into it.

Login or Signup to reply.