#680 Options for meta-object access

jodastephen Thu 23 Jul 2009

Breakout from Symbols.

Fan is a language where reflection and the meta-objects are important. By this I mean the Pod, Symbol, Type, Method and Field objects.

Extending my suggestion makes the # sign the key symbol for access to meta-objects.

Option A, current:

.             => No pod literal

@sys::foo     => Symbol literal
@foo          => Symbol literal

sys::Str#     => Type literal
Str#          => Type literal

sys::Str#foo  => Slot literal
Str#foo       => Slot literal

object.type   => Type from object
.             => No Slot from object

Option B, based on current with a few tweaks:

sys::#        => Pod literal

#@sys::foo    => Symbol literal
#@foo         => Symbol literal

sys::Str#     => Type literal
Str#          => Type literal

sys::Str#foo  => Slot literal
Str#foo       => Slot literal

object#       => Type from object
object#foo    => Slot from object

Option B1, (added) for symbols:

@sys::#foo    => Symbol literal
@#foo         => Symbol literal

Option C, with the # is always at the end:

sys::#        => Pod literal

sys::Str#     => Type literal
Str#          => Type literal

sys::Str.foo# => Slot literal
Str.foo#      => Slot literal

@sys::foo#    => Symbol literal
@foo#         => Symbol literal

object#       => Type from object
object.foo#   => Slot from object

I find the current setup (A) quite confused. and I prefer option C if it can be made to work with the grammar.

tactics Fri 24 Jul 2009

I can see where a pod literal syntax might be nice, but the alternative syntaxes you provide don't seem much more intuitive.

tompalmer Fri 24 Jul 2009

I also prefer the current syntax (A) over the other options given here, although I can see the idea of consistency in option C. And I also agree that pod literal syntax might be handy at times.

And I still think accessing a slot tied to an object might be nice, but I also still think that the whole data binding concept ought to be considered with it. I just haven't thought much about that myself so far.

brian Fri 24 Jul 2009

I don't think we necessarily have everything ironed out, but I'm a bit exhausted by all the symbol/repo work from this month to give it much thought. I need to take a break and revisit it a few weeks.

I am definitely coming around to using # instead of .type to free up type as a valid slot identifier.

Also it sure would be nice to have qualified name syntax match literal syntax (but that ship may have sailed).

tompalmer Fri 24 Jul 2009

If there were a change to allow #object (or object#) on local vars, say, I would recommend it mean a "pointer" to the object, as in, some kind of Field object to let you read and write the object. I don't think such a thing should give the type of the object.

So, for example:

class Refs {
  Void main() {
    num := 1
    numRef := #num
    numRef.set(null, 2)
    echo(num) // Prints 2
  }
}

That seems the most consistent meaning of # if it were to be supported on arbitrary vars.

tompalmer Tue 28 Jul 2009

I've got a story about syntax and symbols and objects/types/pods and literals. It's the not exactly the Fan looks today, and it almost certainly isn't the way it ought to be. Just trying a fresh perspective to see if it sheds light on things.

Symbols aren't slots in pods the way types have slots, though I think it's possible that pods could be const singleton instances of the Pod class or some subtype of Pod. In any case, since pod namespace access today looks like pod::Type or @pod::symbol, is there a reason why pod object access couldn't simply be pod (rather than pod::#)?

But that would make them an odd duck compared to needing special chars (such as # or @) to get at other top-level items.

So, pods are odd in this sense. Maybe could say pod.Type and pretend they really are members, though I've tended to like the pod::Type syntax, and I understand it makes some handling easier.

Now, I want to pretend for a moment that symbols are simply namespaced globals (whether accessed by . or ::) or rather that "global" (under pod namespaces) consts exist in Fan:

** Totally global. Implied const or explicit?
const Float pi = 3.14f

** A class in the same file.
class PiGreeter {
  Void main() {
    echo(pi) // or could be 'myPod::pi' (or 'myPod.pi')
  }
}

Well, then global (or even class member?) field (or even method?) objects could be the typed, namespaced symbols for facets or otherwise:

** Totally for facets.
const Str author = ""

** Using the pod-level field for a facet, using facet syntax.
@author="Me"
class MyClass {
  Void main() {
    // But when I access it, I just use a field literal.
    echo(type.facet(author#))
  }
}

Here I'm using Stephen's postfix syntax. If want the value of author, I reference it like I would use it like a var (that is, author), but if I want it as a symbol, I use the literal (that is, author# or myPod.author# or whatever). And you could perhaps still have virtual pod-level fields related to overriding things for customization. I haven't thought through the best unification theory on that. (Is there such a thing as multiple pod types, and letting one pod subtype another or oddness like that?)

And, anyway, then say we really do just use trailing # for all literals: pod#, field#, SomeType.field#, somePod.SomeType.field#, SomeType.method#, somePod.someField#, and so on.

Again, some of this story doesn't depend on :: vs. ., and I know some of the pain of needing to wait until a name resolution phase to identify pods if . is used.

I'm just looking for an alternative unification theory as food for thought. I don't expect Fan to switch to this style, and it might raise more questions than it could possibly answer. So again, just a mental exercise.

brian Tue 28 Jul 2009

Idealistically pod::Type.slot are all just variables in scope. But in practice having slots or symbols implicitly in your scope would be annoying, so I think we always want a special care to indicate reflection literal.

Ideally this will be done consistently with any qname or unqualified name in scope using a prefix operator like @ or postfix operator like #.

However we have a couple inconsistencies that I see problems with:

  • @Type.slot or Type.slot# would be ambiguous - are you calling a static method or referencing the slot literal
  • symbols are an odd case right now

I haven't given this a lot of thought, but the ideal design comes around to a single operator or syntax mechanism which operates on the follows identifier strings:

pod             =>  Pod 
pod::Type       =>  Type
Type            =>  Type
pod::Type.slot  =>  Slot
Type.slot       =>  Slot
slot            =>  Slot
pod::symbol     =>  Symbol

Note that what we do today is replace . with # to make types/slots work.

tompalmer Wed 29 Jul 2009

By the way, I've thought about how if we don't like the global look of plain symbol for symbol values (rather than the meta-object access), maybe ::symbol would work. I think that gives some intuitive feel about how we're accessing a different scope than just members/locals.

And maybe @symbol is almost a shorthand for #::symbol, and @pod::symbol is also still allowed for general @ usage. Not sure that's ideal, but it's a thought.

As for a common operator for all of it, the problem with prefix operators generally is that they need to bind tightly or you need parens to wrap the scope. Which is why @ is okay for symbols but not ideal for @pod::Type.slot. What object is reflected, Type or slot? Otherwise, I actually sort of like the @ prefix (Pascal-ish) if we don't care about distinguishing symbols from members of the enclosing type.

Oh, and I should bring back up my requested stand-alone # as a reference to the statically enclosing type.

brian Wed 29 Jul 2009

just took a jog and was noodling on this thought:

  1. make symbol literals work just like type literals (except they can only be a literal):
    transient#
    t.facet(simple#)
  2. unify symbol value access and field storage access via @ (moving back to old field storage syntax):
    @transient
    @localizedMessage
    Str field { set { @field = val } }

only issue is that you might have ambiguity b/w field access and symbols if you have conflicting names

tompalmer Wed 29 Jul 2009

The main niceness to @symbol literals is that accessing them in code looks similar to applying them. Though I've also mentioned alternatives above, I'm not sure it's worth giving up that niceness. Something to keep in mind, at least.

tompalmer Wed 29 Jul 2009

Maybe this would work:

// A symbol for facets or whatever.
// Accessed later by '@author' or '@pod::author'.
// No need for value reference.
Str @author

// A configuration field.
// Accessed later by 'serviceUri' (or '::serviceUri'?) or 'pod::serviceUri'.
// Reflected later by 'serviceUri#' (or '::serviceUri#'?) or 'pod::serviceUri#'.
virtual Uri serviceUri := `https://somewhere/serviceUri`

Maybe? Current Symbol would be renamed to Config? And a new separate Symbol (or Sym?) type introduced?

And for pods, configs, types, and slots (but not the new plain symbols), maybe just use the trailing # as suggested by Stephen?

jodastephen Wed 29 Jul 2009

Type.slot# would be ambiguous - are you calling a static method or referencing the slot literal

How would this be a static method call? I'd define it as "everything from the start of the expression to the # is the literal".

make symbol literals work just like type literals

Yep. Sounds good. This moves us closer to all programmatic elements having a consistent literal (just need to add pods).

unify symbol value access and field storage access via @ (moving back to old field storage syntax)

That could work, although it still feels a little odd.

jodastephen Wed 29 Jul 2009

Interestingly, what I wonder is what the difference is between a symbol and a static (const final) field on a class? These have similarities in that they are both namespaced singleton values, essentially immutable (IIUC, symbols are immutable?). Whereas fields are entirely different, being mutable and accessed via properties (which can have manual getter/setter).

Taking this approach leads me to what might be described as "symbols" on both pods and types. Both could be overridden in config files, although the final keyword would prevent this.

pod foo     symbol values overridden in foo.fansym
class Bar   symbol values overridden in Bar.fansym

I know I'd find the ability to override class level constants very useful. The extra namespacing level actually feels more appropriate for config items, and way more powerful. (And would allow for a constants class like FooConfig dedicated to config, if the developer wanted to organise code that way.)

In fact, I wonder if this leads to a separation of facets from symbols? Symbols defined in the pod file would be facets. Symbols defined in a class file would be regular symbols. (We'd probably need some new terminology, but I hope I'm explaining myself:

pod foo {
  myFacet := "HelloFacet"        // virtual by default?
}

class Bar {
  static mySymbol := "HelloSym"  // final by default?
                                 // replaces current static const final var
}

brian Wed 29 Jul 2009

I think the basic idea of treating static consts as symbols at either the pod or type level (or maybe just the type level) is really interesting

lots of different ideas swirling around here:

  • symbols
  • facets
  • configuraiton/localization "constants"
  • static const fields
  • field storage grammar problem
  • pod meta-data
  • reflection syntax unification

I feel we are on the right track, but I am not seeing everything click into place yet

jodastephen Fri 31 Jul 2009

lots of different ideas swirling around here I agree.

Lets work on the basis (for now) that configurable values (avoiding the word symbol just now) on a per field in a class basis is a goal.

Then we need to work out if we need to have separate concepts for static fields (constants) and these configurable values.

Both are static.

Both are effectively immutable. But perhaps we allow mutable configuration values (which are cloned each time they are returned?).

The meta-object for a configurable value is more complex (default value, localisation, etc).

So, are the concepts the same or different?

If different, should we have a different syntax?

tompalmer Fri 31 Jul 2009

The meta-object for a configurable value is more complex (default value, localisation, etc).

I still think that for configuration, the overridden/localized value (if overridden/localized) is by far the most important, and that this value is more important than reflecting a meta-object. (There might still be occasions to get at default values and whatnot, but I think they'd be very rare.)

Login or Signup to reply.