Implement final by default.
Add default & override keywords for overload refinement.
default overloads can be masked by subsequent overloads & overrides.
override overloads mask previous definitions (equivalent to previous overload behaviour).
Add some tests.
Searching for ambiguous overloads exposed an issue with builtin constructors. If a field type is an expression to be evaluated using pattern cells the default constructor will fail. A workaround is to use computed record which doesn't generate a constructor overload.
Updated test/lang/uncategorized/45 to use a computed record definition to prevent generation of a faulty built-in constructer.
Make overloads final by default.
Introduce 'override' keyword to preserve previous overload behaviour.
Introduce 'default' overloads which can be overridden by either overloads or overrides.
Library uses override for now.
Add final tests.
Merge remote branch 'jckarter/master' into final2
Include override & default in claydoc
Sounds good, but the rename of overload to override seems a bit churny to me for little benefit. Can the issue with builtin constructors be detected by the compiler, so it can raise an error if you try to use the builtin constructor when it's not valid?
The overload/override keywords are a bit naff. I have experimented with removing overload (# 457), however the benefits are marginal due to syntactic inconsitencies e.g. standalone definitions, interfaces, overloads. Maybe standalone could be removed, it does seem somewhat redundant with default final and the private overload constraint.
I'm not entirely happy with the default overload. Currently it is available anywhere in the overload sequence. Maybe it would be better to tether a default definition to a define in some way.
It should be possible to detect a faulty generated constructor. A flag on the overload or some thing more sophisticated, I'll open a new ticket and take a look at it.
To be clear, the overload keyword is still in use here.
overload is the standard way to overload a symbol, however it can only mask a default e.g.
default foo(T) = false;
overload foo(#Int32) = true; //Valid override of foo
overload foo(#Int64) = true; //Valid override of foo
override masks any prior definition e.g.
overload foo(a:Int32, b:Float64) = ...;
overload foo(a, b) = ...; //Invalid as overload cannot override
override foo(a, b) = ...; //Valid overriding of previous foo(a:T, b:U) overloads
I've changed all overloads in the library/compiler to be override's as this preserves the existing functionality. All modules will need to be reviewed to take advantage of default final.
I see; that makes sense. Sounds good to me.
Update compiler overload status flags.
Fix libs that use records with properties. Provide default overloads.
Only check for ambiuous matches when match isn't an override.
Re-enable dodgy tests
Merge branch 'master' into final2
@jckarter @stepancheg @galchinsky Any objections to merging this prior to library overhaul? Any issues I may have missed?
@agemogolk I (as usually) would like to complain about quality of commits.
And the worst thing it is really hard to understand exact semantic of default/overload/override keywords from your description. Compare to description of my original proposal: here.
Good description should contain strict description of algorithm, and should be illustrated by code examples (tests are not enough). And such description should be included in commit message for people who read commit history. This description can be later copied into the language specification.
Bottom line: I like it, but I'm afraid I missed something important.
@stepancheg constructive criticism is always welcome.
Sorry the explanation of semantics is not clear to you. In answer to your queries:
@agemogolk Well, I think override that can override anything conflicts with basic idea of "final overloads": final overloads should not be overloadable. This is the point of final overloads: if you see invocation foo(1), and you see final overload foo(param), you can be sure that no other part of project changes behavior of foo(1). Truly final overloads make code easier to read and maintain.
I prefer previous version of this feature. Repeating myself:
foo(x) = 1; // standalone
overload foo(x, y, z) = 2; // ERROR: any overload of foo is invalid
default bar(x) = 1; // default overload
default bar(x) = 2; // it is OK to override default overload with another default overload
overload bar(x: Int) = 3; // final overload for Int parameter
[S when String?(S)]
overload bar(x: S) = 4; // final overload for String? parameter
overload bar(s: CStringRef) = 5; // ERROR: because overload for String? is final
I'm with @stepancheg that it'd be ideal to only have 'default' and 'non-default' overloads, which (if I understand correctly) correspond to default and overload in this scheme. However, override seems like a good migration shim that preserves the old behavior while the library gets overhauled. If override can be completely replaced by default or the new overload in the library, then it'd make sense to drop it once the library's been migrated. That sound good to both of you?
I'm not sure that temporary override keyword won't become permanent.
There's easier migration path.
From time to time try to compile tests with -overload-is-final option on, fix library by replacing some overload with default
When all tests become working with -overload-is-final option on, drop the option "-overload-is-final", and make violation of final overload unconditional error
@agemogolk I forgot to mention, that I think that default overload should be able to override another default overload. Use case:
define Printable?(x): Bool;
default Printable?(x) = false;
[S when Sequence?(S)]
default Printable?(#S) = true; // overrides previous default
overload Printable?(#InfiniteSequence[A]) = false;
default and overload are a bit similar to virtual and (no-specifier-that-means-final) in C#: you may override virtual with another virtual or final, and you cannot override final.
I agree with @stepancheg's approach, temporary keywords should be avoided. @jckarter, to be clear, do you agree that default should be allowed to override another default.
@stepancheg I think there is some confusion going on, my original proposal was to have default be overridable by an other type of overload, apologies if that wasn't clear. All that needs to happen is removal of override.
I think that ideally you'd have it so that default overloads can't override each other either, so that there are most two overloads that can influence any call site. In practice I don't know if that'll work out because of cases like @stepancheg's example, so I'm OK with defaults being able to override each other. Maybe an alternative sanity constraint would be to limit default overloads so that they can only appear in the same module as the symbol definition.
Constraining default to a single module sounds reasonable. I'll remove the override keyword, tidy-up and add the compiler flag as per @stepancheg suggestion and see how it looks.
@jckarter @agemogolk I think default overloads limited to one module is too restrictive. In my example above (about Printable?), printable module (that defines Printable?) may not know about sequences, thus printable module cannot add second default overload.
@stepancheg You're right. You need to be able to refine concepts independent of the original module, so limiting defaults isn't going to work.
See issue #478 for updated implementation.