gracelang / language

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

missing required methods? #145

Closed kjx closed 6 years ago

kjx commented 6 years ago

should this code "compile" - and more importantly, why?

class sup {
    method x {y}
}

class sub {
    inherit sup
    method y { print "y" } 
}

sub.x

amg give a compiles time error at line 2, saying "unknown variable or method 'y'". Kernan crashes at runtime with "No receiver found for y". Why? Where does it say so in the spec? Which behaviour should be mandatory for all implementations, or option (i.e. could be in a dialect)?

I seem to remember a discussion that this kind of program should be OK if everything was untyped.

Both Kernan & amg will accept and run a program where line 2 becomes method x {self.y}. Why?. I can (sort of) see why that should be acceptable, again especially in a untyped context, but don't see why the implicit send version above should not also be acceptable. At this point, we have all the lexical context for sup so we know y is not defined in an outer lexical scope. We also know (in this example, for sure) that y is not defined in a superclass of sup (there isn't one) but of course y could be defined in a subclass, indeed, here it is defined in sub.

Perhaps yshould be defined asrequired in sub - but if it isn't, why is writing self.y OK but just 'y' is not? and where is that distinction defined?

Finally, here's the complementary program:

class sup {
    method x {outer.y}
}

Both amg and Kernan accept this at compile time but crashes with a runtime error. if we want any of these kind of things to be a "compile" error - shouldn't it be this one? we can be absolutely sure (modulo linking to different version of a dialect) that y is NOT anywhere in the outer of sup. (see also #59 #102)

kjx commented 6 years ago

See also #32 and #59. Back in 2015, Andrew thought required methods should be optional (https://github.com/gracelang/language/issues/32#issue-106885595). I can't see anything in the spec saying whether required methods are required or are optional.

Another way to put this is the question of whether the resolution of an internal request (implicit request), that, according to the current spec

"must resolve to one of the former cases"

(i.e. a self or an outer request)

must be manifest (resolved early) or can be resolved late?

Kim (https://github.com/gracelang/language/issues/59#issuecomment-186087713) once suggested that "If a construction introduces an error that cannot be fixed, then it should be declared erroneous at that point".

Things like missing required methods, some (all?) structure clauses, etc, can actually always be "resolved later" provided the class with the problem is abstract, and provided concrete subclasses always fix the problem. The example at the top of this issues has he sup class with a "missing abstract method" error, and its subclass sub resolves that problem by defining the method.

KimBruce commented 6 years ago

I have a vague memory of a discussion where (I believe) there was some agreement that in the example above, we would need to write

method y {required}

or similar syntax, but that one could create an object from sup that would work fine as long as there was no call of y. The static typing discipline would most likely bar creation of an object from such a class, but a more dynamic version would allow the creation so you could test the methods as they are (slowly) added.

I don't recall that we ever discussed labeling classes/objects as "abstract".

I so agree that there should be no difference between calling y and self.y if the former is resolved to a call of self.

kjx commented 6 years ago

I have a vague memory of a discussion

me too. but I also thought we were switching back to abstract as the keyword. (At this point, I'd dump conceptual purity and make it an annotation that requires a null method body)

but that one could create an object from sup that would work fine as long as there was no call of y

as you said somewhere else I can't find - the problem with allowing the dynamic interpretation is we have to provide semantics for it. In this case that's not really a problem. (actually, the "core Grace" I have in my head is so simple that in most cases the semantics aren't really a problem, which makes we wonder why we necessarily restrict things).

I don't recall that we ever discussed labeling classes/objects as "abstract".

we didn't - I recall an old discussion thinking that we couldn't - if we could annotate objects then we could.

I so agree that there should be no difference between calling y and self.y if the former is resolved to a call of self.

aye ,there's the rub. How do you know if y means the same as self.y?
I think the answer is - you know by manifest but manifest breaks modularity (see Gilad). Another answer is: you know y must mean self.y if y couldn't mean outer*.y and you can pretty much always know that given a module and its dialect. Inheritance can cross module boundaries.

kjx commented 6 years ago

the only possible way to make progress is to adopt the minigrace design