gracelang / language

Design of the Grace language and its libraries
GNU General Public License v2.0
6 stars 1 forks source link

alias and exclude in objects (low priority placeholder) #144

Closed kjx closed 6 years ago

kjx commented 6 years ago

We could allow alias and exclude in objects (and classes). Consider:

object {
   method m { ... }
   alias n = m 
   exclude x
}

This would give the object a method n with the same logical body as m, and a confidential,required method x.

I'm not actually keen on this as a design - although what's odd is that writing exclude x is pretty close to writing method x required (or whatever the final syntax is).

Alias clauses in objects would allow ambiguous outer requests to be disambiguated on the declaration side (rather than the call side). E.g. something like

class out { 
  method m { ... } 
  class in { 
       inherit super //super presumably defines `m`
       method test { m }  //m is ambiguous
       }
}

note that out.in.test will result in an ambiguous internal request of m.

The example can be rewritten (only changing local lexical scope) to something like this:

class out { 
  method m { ... } 
  alias out_m = m 
  class in { 
       inherit super //super presumably defines `m`
          alias super_m = m 
       method test {
          out_m  // unambiguously goes out lexically
          super_m // unambiguously goes up inheritance 
          m // still ambiguous, thus should be an error
          }          
}

where programmers can now get around the ambiguity (but without using special self or outer requests). If programmers really want the inner object to have an m method they could add in either

       method m { super_m }   

(or almost equivalently)

      alias m = super_m

to go up inheritance, or

      method m { out_m }

to go out lexically. We could even twist alias to allow alias m = out_m in this case, but that's probably one step to far.

kjx commented 6 years ago

Let's be clear, we don't really need this as a language feature. Object level exclude x can always be written method x is confidential required. Object level alias n = m can nearly always be written method n { m }, and where that doesn't work (e.g., to protect against subsequently overriding of m) can be sorted with more aliases on inheritance clauses, or renaming m as something horrible like myFoo_m, writing method m { myFoo_m } and being careful never to override myFoo_m. If we had a final annotation on methods, you'd make myFoo_m final.

apblack commented 6 years ago

Let's be clear, we don't really need this as a language feature.

Wasn't one of our original goals (design principles, whatever) to keep Grace as simple as possible?

I can see the motivation for the alias clause in an object, but one can get the same effect by defining a block in the object constructor, and then having two methods that both apply that block. This is proof against the overriding of one or the other method.

As for allowing exclude x in an object that does not define x: that seems daft. In fact, we already say that excluding x when x is not present is an object composition error.

You do bring up one good point, though: the spec also says that excluded attributes are replaced by a confidential, required method. This seems wrong to me; if one wanted a confidential, required method, one could define one, which would override the reused attribute, and there would be no need for exclude. I think that we need exclude exactly when the attribute being excluded is not required. Of course, I don't yet have much experience with exclude, so my view on this may change.

kjx commented 6 years ago

Wasn't one of our original goals (design principles, whatever) to keep Grace as simple as possible?

absolutely! That's certainly my motivation e.g. behind reconsidering self and outer requests #140

I can see the motivation for the alias clause in an object,

actually my main motivation was to have a lexical "parallel" of alias...

As for allowing exclude x in an object that does not define x: that seems daft. In fact, we already say that excluding x when x is not present is an object composition error.

Yep. On reflection it probably wouldn't work the way I want it to - if i define a required/abstract method in an object, doesn't that definition override all inherited definitions (even if there is a non-abstract inherited definition). Hmm: the spec says it does. This is why I'm trying to come up with some kind of simple formal/operation definition of what these things do - so we have something concrete to argue about.

The problem with the current local-required-overrides-everything semantics are that I cannot then write a declaration to ensure (or at least hint) a receiverless request will be resolved via inheritance in a concrete object. I can do it on the use side (by requesting self.x, assuming we either don't have the Eiffel rule, or x is public); I can do it in a a trait or abstract class by declaring a required method but I can't see a way to say "I expect all requests of x to be resolved via inheritance, not lexically" - and either make it so, irrespective of lexical declarations, or at least ensure an ambiguity error is raised.

In the following, we expect encl2.sub.test to always resolve to "encl.x", right? incl can indicate it wants to resolve x via inheritance by declaring an abstract method. I can't see way for sub to say "resolve x to a (future overriding) subclass definition, or failing that an inherited declaration from any parent".

def encl = object {
   method x { print "encl.x" }
   class incl {
       method test { x } 
  }
}

def encl2 = object { 
  method x { print "encl2.x" }
  class sub {
     inherit encl.incl 
     method x { print "sub.x" }
  }
}

sub.test

OK, on more reflection, could sub do:

  class sub {
     inherit encl.incl 
       alias super_x = x  
     method x { super_x }
  }

would that do what I want?

You do bring up one good point, though: the spec also says that excluded attributes are replaced by a confidential, required method.

Isn't the declaration coming down from the inherited/used "super-part-object" replaced, not the local declaration in the inheriting/using object?

I remain concerned that the "tentative" multi-stage reuse semantics are not "as simple as possible".

apblack commented 6 years ago

would that do what I want?

I don't really know what you want, or the point of this "issue". If it really is a low priority placeholder for you, maybe I should just ignore it?

kjx commented 6 years ago

I don't really know what you want, or the point of this "issue".

What I want is mostly from #140 - are alias and exclude (on inheritance) and other coding workarounds (on enclosing lexical scopes) enough that we can do without special self and outer requests? (We'd still have self and outer but as pseudo-defs, not as request types --- you wouldn't lose anything by putting them in another def and then doing the request on that def).

If it really is a low priority placeholder for you, maybe I should just ignore it?

sure.

kjx commented 6 years ago

another example of "kjx's useless, distracting, and pointless F**king around" as we put it in the discussion this morning