[Pitch] Moving where Clauses Out Of Parameter Lists


(Robert Widmann) #1

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?

~Robert Widmann


(Joe Groff) #2

I think this is a good idea, though I would put the `where` clause after the function signature:

func foo<T: Foo, U: Bar>(x: T, y: U) -> Result<T,U>
    where T.Foo == U.Bar /*, etc. */
{
}

As others noted, it's also appealing to do this for type declarations too:

struct Foo<T: Foo, U: Bar>
    where T.Foo == U.Bar
{
}

and that gives a consistent feeling with extensions and protocol declarations.

-Joe

···

On Apr 6, 2016, at 11:30 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?


(Erica Sadun) #3

+1.

-- E

···

On Apr 6, 2016, at 12:30 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

Thoughts?


(Sean Heber) #4

*grabs paintbrush*

Something that has always bothered me about the generic syntax for functions is how far the function’s name is from its parameters. There are probably reasons why the following might not work, but it could address my desire to keep the name of the thing close to the names of the inputs:

func <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
anyCommonElements(lhs: T, _ rhs: U) -> Bool {
}

I haven't given this tons of thought, but this seemed like a good a thread as any to mention my concern about the distance of the name from the parameters. :slight_smile:

l8r
Sean

···

where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

On Apr 6, 2016, at 1:30 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #5

I don’t see much value in moving them out of the angle brackets but preserving the position; consider:

  // status-quo:
  func anyCommonElements <T: SequenceType, U: SequenceType
    where
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element>
    (lhs: T, _ rhs: U) -> Bool
  
  // as-per proposal:
  func anyCommonElements <T: SequenceType, U: SequenceType>
    where
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element
    (lhs: T, _ rhs: U) -> Bool

…not very different! Or, if you want it with fewer lines:

  // status-quo:
  func anyCommonElements <T: SequenceType, U: SequenceType
    where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element>
    (lhs: T, _ rhs: U) -> Bool
  
  // as-per proposal:
  func anyCommonElements <T: SequenceType, U: SequenceType>
    where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
    (lhs: T, _ rhs: U) -> Bool

…still not very different!

Moving the `where` to come after “everything else" seems like an improvement for functions:

  func anyCommonElements <T: SequenceType, U: SequenceType>(lhs: T, _ rhs: U) -> Bool
    where
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element {
  // body here...
}

…and presumably-also for type declarations:

  class SomeClass<A,B,C,D,E> : BaseClass, SomeProtocol, AnotherProtocol
    where
    A.Something == SomethingElse,
    B.Input == C.Output {

  }

..,but would take some getting-used-to.

Losing the ability to write `:` constraints inline in the brackets seems a non-starter, but perhaps constraints within the brackets could be limited to `:` constraints?

···

On Apr 6, 2016, at 1:30 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #6

A bit of a meta-argument:

It is very common to use single-capital letter generic parameters, and the API style does not give guidance around the naming of generic parameters.

However in my humble-but-opinionated view, they are effectively scope-bound, dynamic type aliases. Declaring func "foo<T>" is like declaring “var i”, but its forgiven since coming up with a good, concise name for such a type alias can be hard.

The standard library seems inconsistent about this as well:

func == <A : Equatable, B : Equatable>(_: (A, B), rhs: (A, B))

vs.

func == <Key : Equatable, Value : Equatable>(_: [Key : Value], rhs: [Key : Value])

The argument I bring up is that naming of the generic parameters may wind up affecting whether the code is clearer having type constraints and the where clause within the brackets or trailing the function. It is important to take this into account and compare both apples to apples and oranges to oranges when evaluating syntax.

(or, change the API guide and standard library to discourage either apples or oranges)

-DW

···

On Apr 6, 2016, at 1:36 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I think this is a good idea, though I would put the `where` clause after the function signature:

func foo<T: Foo, U: Bar>(x: T, y: U) -> Result<T,U>
   where T.Foo == U.Bar /*, etc. */
{
}


(Matt Whiteside) #7

+1 to moving the `where` clause after the function signature.

-Matt

···

On Apr 6, 2016, at 12:36, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 6, 2016, at 11:30 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?

I think this is a good idea, though I would put the `where` clause after the function signature:

func foo<T: Foo, U: Bar>(x: T, y: U) -> Result<T,U>
   where T.Foo == U.Bar /*, etc. */
{
}

As others noted, it's also appealing to do this for type declarations too:

struct Foo<T: Foo, U: Bar>
   where T.Foo == U.Bar
{
}

and that gives a consistent feeling with extensions and protocol declarations.

-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Pyry Jahkola) #8

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations.

+1 already!

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

I would actually move them as far as after everything else, and right before the definition body. For the above function that would mean:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool
    where T : SequenceType,
          U : SequenceType,
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element
{
    ...
}

That would make the definition look closer to what the call site looks like.

The same would work for generic types too:

public struct Dictionary<Key, Value>
    where Key : Hashable
{
   ...
}

— Pyry

···

On 06 Apr 2016, at 21:30, Developer via swift-evolution <swift-evolution@swift.org> wrote:


(Pyry Jahkola) #9

Joe,

Just from your experience on this topic, is there any reason not to also move the primary constraints into the trailing `where` clause?

So instead of what you wrote, we'd have it this way:

    func foo<T, U>(x: T, y: U) -> Result<T,U>
       where T: Foo, U: Bar, T.Foo == U.Bar /*, etc. */
    {
    }

…as well as:

    struct Foo<T, U>
       where T: Foo, U: Bar, T.Foo == U.Bar
    {
    }

Like I said earlier in this thread, I think this would also make the `extension` syntax more uniform with types (by turning generic parameters into strictly locally visible things):

    extension Foo<T, U> where U == Baz { // (Could've used X and Y here as well.)
        // Now it's clear where the names T and U come from.
        var bazzes: [U] { return ... }
    }

— Pyry

···

I think this is a good idea, though I would put the `where` clause after the function signature:

func foo<T: Foo, U: Bar>(x: T, y: U) -> Result<T,U>
   where T.Foo == U.Bar /*, etc. */
{
}

As others noted, it's also appealing to do this for type declarations too:

struct Foo<T: Foo, U: Bar>
   where T.Foo == U.Bar
{
}

and that gives a consistent feeling with extensions and protocol declarations.


(Chris Lattner) #10

+1, long overdue. Please keep basic constraints (ones expressible without a ‘where’ clause, like simple conformances) inline though.

-Chris

···

On Apr 6, 2016, at 11:30 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations. For example, the Swift book implements this example to motivate where clauses

func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool

This is noisy and uncomfortable to my eyes, and almost impossible to align correctly. Per a short discussion on Twitter with Joe Groff and Erica Sadun, I'd like so see what the community feels about moving the where clause out of the angle brackets. So that example becomes

func anyCommonElements <T: SequenceType, U: SequenceType>
where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

Thoughts?


(Timothy Wood) #11

... though if there are no type constraints, this would be ambiguous:

  // If there are no know types T and R in scope, is this an error or are T and R assumed to be unconstrained generic parameters?
  func f(arg: T) -> R

-tim

···

On Apr 6, 2016, at 11:48 AM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

This almost seems like it could work so that it didn't even need the bracketed parts to be able to figure out the types:

func anyCommonElements(lhs: T, _ rhs: U) -> Bool where
   T : SequenceType,
   U : SequenceType,
   T.Generator.Element: Equatable,
   T.Generator.Element == U.Generator.Element
{}


(Joe Groff) #12

It's a judgment call. It's my feeling that in many cases, a generic parameter is constrained by at least one important protocol or base class that's worth calling out up front, so it's reasonable to allow things like 'func foo<C: Collection>(x: C) -> C.Element' without banishing the 'Collection' constraint too far from the front of the declaration.

-Joe

···

On Apr 6, 2016, at 12:52 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

Joe,

Just from your experience on this topic, is there any reason not to also move the primary constraints into the trailing `where` clause?

So instead of what you wrote, we'd have it this way:

    func foo<T, U>(x: T, y: U) -> Result<T,U>
       where T: Foo, U: Bar, T.Foo == U.Bar /*, etc. */
    {
    }

…as well as:

    struct Foo<T, U>
       where T: Foo, U: Bar, T.Foo == U.Bar
    {
    }

Like I said earlier in this thread, I think this would also make the `extension` syntax more uniform with types (by turning generic parameters into strictly locally visible things):

    extension Foo<T, U> where U == Baz { // (Could've used X and Y here as well.)
        // Now it's clear where the names T and U come from.
        var bazzes: [U] { return ... }
    }


(Sean Heber) #13

This almost seems like it could work so that it didn't even need the bracketed parts to be able to figure out the types:

func anyCommonElements(lhs: T, _ rhs: U) -> Bool where
    T : SequenceType,
    U : SequenceType,
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element
{}

.. which would address my concerns about distance from names to params, too!

l8r
Sean

···

On Apr 6, 2016, at 1:47 PM, Milos Rankovic via swift-evolution <swift-evolution@swift.org> wrote:

On 6 Apr 2016, at 19:35, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool
    where T : SequenceType,
          U : SequenceType,
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element
{
    ...
}

This is an excellent idea: +1!

If `where` is left on a previous line, it would also appear more in line with `throws`:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool where
    T : SequenceType,
    U : SequenceType,
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element
{
    ...
}

milos
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Sean Heber) #14

I would think it’d just be unconstrained generic parameters - same as this is now:

func f<A, B>(lhs: A, _ rhs: B) {}

However I can see how it’d certainly be potentially too confusing. It might be worthwhile to retain the brackets, but require type restrictions to be moved to the trailing “where” so that it keeps the <> part as short as possible, so this would be an error:

func f<A: SequenceType, B: Equatable>(lhs: A, _ rhs: B) -> Bool {}

And would have to be written:

func f<A, B>(lhs: A, _ rhs: B) -> Bool where A: SequenceType, B: Equatable {}

Maybe? That’s a bit longer, though. Maybe it’s not worth changing that aspect of it. I like moving “where” to the end, though.

l8r
Sean

···

On Apr 6, 2016, at 1:53 PM, Timothy Wood <tjw@me.com> wrote:

On Apr 6, 2016, at 11:48 AM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

This almost seems like it could work so that it didn't even need the bracketed parts to be able to figure out the types:

func anyCommonElements(lhs: T, _ rhs: U) -> Bool where
  T : SequenceType,
  U : SequenceType,
  T.Generator.Element: Equatable,
  T.Generator.Element == U.Generator.Element
{}

... though if there are no type constraints, this would be ambiguous:

  // If there are no know types T and R in scope, is this an error or are T and R assumed to be unconstrained generic parameters?
  func f(arg: T) -> R

-tim


(Erica Sadun) #15

From a reading point of view, it's always better to declare tokens before using them. This groups them with the parameters (and the parameters in turn may use the tokens), so the scope-specific vocabulary is all laid out in front.

-- E

···

On Apr 6, 2016, at 2:03 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 6, 2016, at 12:52 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

Joe,

Just from your experience on this topic, is there any reason not to also move the primary constraints into the trailing `where` clause?

It's a judgment call. It's my feeling that in many cases, a generic parameter is constrained by at least one important protocol or base class that's worth calling out up front, so it's reasonable to allow things like 'func foo<C: Collection>(x: C) -> C.Element' without banishing the 'Collection' constraint too far from the front of the declaration.


(Robert Widmann) #16

Even better. +1.

~Robert Widmann

2016/04/06 14:35、Pyry Jahkola via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

On 06 Apr 2016, at 21:30, Developer via swift-evolution <swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic interface to a thing and you need to constrain it, possibly in an extension, maybe for a generic free function or operator, you know what a pain the syntax can be for these kinds of operations.

+1 already!

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

I would actually move them as far as after everything else, and right before the definition body. For the above function that would mean:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool
    where T : SequenceType,
          U : SequenceType,
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element
{
    ...
}

That would make the definition look closer to what the call site looks like.

The same would work for generic types too:

public struct Dictionary<Key, Value>
    where Key : Hashable
{
   ...
}

— Pyry
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Juan Ignacio Laube) #17

+1. I like the idea of seeing the function signature first, then the constraints.

···

On Apr 6, 2016, at 3:47 PM, Milos Rankovic via swift-evolution <swift-evolution@swift.org> wrote:

On 6 Apr 2016, at 19:35, Pyry Jahkola via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool
    where T : SequenceType,
          U : SequenceType,
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element
{
    ...
}

This is an excellent idea: +1!

If `where` is left on a previous line, it would also appear more in line with `throws`:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool where
    T : SequenceType,
    U : SequenceType,
    T.Generator.Element: Equatable,
    T.Generator.Element == U.Generator.Element
{
    ...
}

milos
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #18

I would actually move them as far as after everything else, and right before the definition body. For the above function that would mean:

func anyCommonElements<T, U>(lhs: T, _ rhs: U) -> Bool
    where T : SequenceType,
          U : SequenceType,
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element
{
    ...
}

That would make the definition look closer to what the call site looks like.

The same would work for generic types too:

public struct Dictionary<Key, Value>
    where Key : Hashable
{
   ...
}

Another nice thing about this style is that, in principle, I think it could be extended to specify requirements on non-type parameter values.

  func ..< <Element: Incrementable>(lhs: Element, rhs: Element) -> Range<Element>
    where Element: Comparable, lhs <= rhs {
    …
  }

I'm not saying we must or even should include that feature, merely that it gives us a nice syntactic slot to use if we choose to do so later.

···

--
Brent Royal-Gordon
Architechies


(Erica Sadun) #19

I'll keep this short. IMO:

* Dictionaries have semantics, so Key/Value makes sense.
* Truly "generic" equatable values do not, so A and B are simple stand-ins.
* Always prefer named tokens when there are actual semantics (Element, Wrapped, etc).

This may or may not be appropriate for inclusion in the API guidelines.

-- E

···

On Apr 6, 2016, at 5:45 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 6, 2016, at 1:36 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I think this is a good idea, though I would put the `where` clause after the function signature:

func foo<T: Foo, U: Bar>(x: T, y: U) -> Result<T,U>
   where T.Foo == U.Bar /*, etc. */
{
}

A bit of a meta-argument:

It is very common to use single-capital letter generic parameters, and the API style does not give guidance around the naming of generic parameters.

However in my humble-but-opinionated view, they are effectively scope-bound, dynamic type aliases. Declaring func "foo<T>" is like declaring “var i”, but its forgiven since coming up with a good, concise name for such a type alias can be hard.

The standard library seems inconsistent about this as well:

func == <A : Equatable, B : Equatable>(_: (A, B), rhs: (A, B))

vs.

func == <Key : Equatable, Value : Equatable>(_: [Key : Value], rhs: [Key : Value])

The argument I bring up is that naming of the generic parameters may wind up affecting whether the code is clearer having type constraints and the where clause within the brackets or trailing the function. It is important to take this into account and compare both apples to apples and oranges to oranges when evaluating syntax.

(or, change the API guide and standard library to discourage either apples or oranges)

-DW


(Shawn Erickson) #20

I very much like this suggestion.

···

On Wed, Apr 6, 2016 at 11:36 AM Pyry Jahkola via swift-evolution < swift-evolution@swift.org> wrote:

On 06 Apr 2016, at 21:30, Developer via swift-evolution < > swift-evolution@swift.org> wrote:

If you've ever gotten to the point where you have a sufficiently generic
interface to a thing and you need to constrain it, possibly in an
extension, maybe for a generic free function or operator, you know what a
pain the syntax can be for these kinds of operations.

+1 already!

Or, if you're feeling ambitious, even

func anyCommonElements <T, U>
where T : SequenceType, U : SequenceType,
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element
(lhs: T, _ rhs: U) -> Bool

I would actually move them as far as after everything else, and right
before the definition body. For the above function that would mean:

func anyCommonElements<T, U>*(lhs: T, _ rhs: U) -> Bool*
* where T : SequenceType,*

* U : SequenceType, T.Generator.Element: Equatable,*

* T.Generator.Element == U.Generator.Element*
{
    ...
}

That would make the definition look closer to what the call site looks
like.

The same would work for generic types too:

public struct Dictionary<Key, Value>
    where Key : Hashable
{
   ...
}