I am not proposing a change to the type system at all. Absolutely no change
whatsoever in any form. I don't know how to say this more clearly.
Therefore your example of:
let f = { x in { y in y } }
Remains illegal and is treated by the compiler exactly like it is currently
treated and like it currently does results in "error: type of expression is
ambiguous without more context".
Where I am proposing a change is that if a closure with generic arguments
is encountered it is transformed into the equivalent struct and the struct
is typed as it currently is (or if there is a better implementation
something equivalent to this), therefore zero change to the type system.
The changes proposed are a transformation into a struct and name mangling,
e.g.:
let increment: <T>(T) throws -> T where T: Numeric = { $0 + 1 }
let increment = { <T>(n: T) throws -> T where T: Numeric in n + 1 }
let increment: <T>(T) throws -> T where T: Numeric = { <T>(n: T) throws
-> T where T: Numeric in n + 1 }
Are all the same and would therefore all become:
struct _Function1__T1__T1__T1__E__Numeric<T> where T: Numeric { //
Mangle name
let call: (T) throws -> T = { $0 + 1 }
}
Which is a trivial transformation; a simple one to one correspondence.
The hard part is actually mangling the name into a canonical form, I
propose:
1. If the function type has 1 argument its name begins `_Function1_`, 2
arguments; `_Function2_`, etc.
2. The 1st generic type is called `_T1_`, 2nd; `_T2_`, etc.
3. A function that doesn't return anything has a return type of `_V_`.
4. Other types are their normal name pretended with an underscore, i.e.
`Numeric` becomes `_Numeric` in running example.
5. The function argument types are then listed in order followed by the
return type, i.e. `_T1__T1_` which follows `_Function1_` in example.
6. Type constraints are converted to the equivalent where clause, e.g.
`let increment: <T: Numeric>(T) throws -> T` is converted to `let
increment: <T>(T) throws -> T where T: Numeric`.
7. The where clause is converted to the converted names, e.g. `where T:
Numeric` becomes `where _T1_: _Numeric`.
8. The where clause is sorted so that constraints on T1 are listed first
etc., e.g. `where _T2_: _Numeric, _T1_: _Numeric` becomes `where _T1_:
_Numeric, _T2_: _Numeric`.
9. Relationships between types are sorted, e.g. `where _T2_.Element ==
_T1_.Element` becomes `where _T1_.Element == _T2_.Element`.
10. The `where` keyword and spaces are deleted and `:` becomes `_E_`, `.`
becomes `_D_`, and `,` becomes `_C_`, the final part of the running example
is therefore `_T1__E__Numeric`.
Since the name mangling is the tricky bit this would be the main work to
see if the above scheme is robust.
But wouldn't it be great to have generic closures :).
-- Howard.
···
On 15 November 2017 at 19:11, Robert Widmann <devteam.codafi@gmail.com> wrote:
~Robert Widmann
2017/11/14 22:02、Matthew Johnson via swift-evolution <
swift-evolution@swift.org>のメール:
Sent from my iPhone
On Nov 14, 2017, at 6:56 PM, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:
Having read all the arguments for what to add to local functions it still
strikes me as a poor use of engineering resources to fix them (though I do
agree they have problems). A better use of resources would be:
1. Deprecate local functions.
2. Allow closures when assigned to a function type to be:
2a. Recursive.
2b. Annotatable with:
2bi. @inline
2bii. @escaping
2c. Generic.
That would be a similar engineering effort and give a better short term
result of better closures which would be much more widely applicable as
well as addressing the issues with local functions.
I believe generic closures would require adding higher rank types to
Swift. That would be pretty cool but I suspect the engineering effort is
at least an order of magnitude greater than the changes discussed in this
thread.
100% correct. Slava raises good points about implementation, I’ll raise
one about semantics:
Without sufficient restrictions on this kind of polymorphism, type
checking will become significantly more difficult for little corresponding
benefit. Enabling the formation of polymorphic types just because you’re
near an arrow means all sorts of fun things now get to happen
let f = { x in { y in y } }
This program is illegal without a type signature, but still *typeable *in
that Swift will try to look for bindings to the tn’s in this signature
f : <T0, T1> (T0) -> (T1) -> T1
This is good - this is the most general unifier for this expression
promised to us by the “Hindley-Milner-like” label we have in the type
checker docs.
Given even rank-2 types, the most general unifier is (ideally) now
f : <T0> (T0) -> (<T1> (T1) -> T1)
Which is significant because despite an obvious isomorphism, the former is
*not* a specialization of the latter. It is a completely separate
polytype that would require additional structural rules to recover the
correct behavior. This can also block inference or generate
counterintuitive unifiers if we’re forced to e.g. unify nested polytypes
against each other.
~Robert Widmann
It also gives a better long term result of not having to maintain local
functions.
-- Howard.
On 15 November 2017 at 09:08, Alex Lynch via swift-evolution < > swift-evolution@swift.org> wrote:
The inference algebra just suggested was enjoyable to read, but is still
a new syntax. Which is interesting and deserving of its own proposal. The
purpose of this proposal is simply to introduce the existing capture syntax
to local functions. Thanks to everyone's feedback pointing out that the
`self` reference analysis is a deeper question than initially realized.
Alex
On Tue, Nov 14, 2017 at 4:36 PM, Mike Kluev via swift-evolution < >> swift-evolution@swift.org> wrote:
On 14 November 2017 at 21:02, David Hart <david@hartbit.com> wrote:
I’d be very hesitant to introduce this syntax:
- it’s new syntax, so it comes with a complexity tax (it isn’t
naturally obvious what it means for a func to be weak)
- it’s only sugar for the capture of self
it might cover well over 90% of use cases (by my "pessimistic"
estimate)... if someone has a quick way to scan and analyse, say, github
swift sources we may even know that current percentage number of real life
usage.
- it doesn’t transpose well to local closures
the last one - maybe not. follow me:
let closure = { [weak self, bar] in ... }
which today can be written as:
let closure = { [weak self, bar] () -> Int in ... } // full form
or as:
let closure: () -> Int = { [weak self, bar] in ... } // full form
which allows this change:
let closure: [weak self, bar] () -> Int = { ... } // full alt form
or in alternative form:
let closure: weak () -> Int = { [bar] in ... } // short hand form
same can be with functions:
func fn() -> Int { [weak self, bar] in ... } // full form
weak func fn() -> Int { [bar] in ... } // short hand form
the two capture attributes will in practice be "close" to each other:
weak func fn() {
[bar] in
....
}
and in majority of cases there will be only weak self:
weak func fn() {
....
}
Mike
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution