Re: [swift-evolution] Feedback on SE-0166 and SE-0167

2017-05-27 Thread Itai Ferber via swift-evolution
Excellent! Glad that ended up working out — looking forward to seeing 
this improve quality of life for GRDB users and others. :)


On 27 May 2017, at 3:40, Gwendal Roué wrote:


> Le 27 mai 2017 à 11:59, David Hart  a écrit :


I didn’t know that was possible either! Really cool. Even better:

 if let databaseValueType = T.self as? DatabaseValueConvertible.Type 
{
 let databaseValue: DatabaseValue = row.value(named: 
key.stringValue)

 return databaseValueType.fromDatabaseValue(databaseValue) as! T
 } else { … }



Yes, that's even more clear like that :-)

And for Encodable support, it works just great (not a single line of 
boilerplate code):


private enum Color: String, DatabaseValueConvertible, Encodable {
case red, green, blue
}

private struct EncodableStruct : Persistable, Encodable {
static let databaseTableName = "myTable"
let name: String
let color: Color?
}

try dbQueue.inDatabase { db in
try EncodableStruct(name: "Arthur", color: .red).insert(db)
}

Gwendal



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


Re: [swift-evolution] Revisiting SE-0110

2017-05-27 Thread Dave Abrahams via swift-evolution

on Thu May 25 2017, Gwendal Roué  wrote:

>> Furthermore, this probably comes up most commonly with dictionaries,
>> since they're a sequence of tuples. The element tuple for
>> dictionaries has element labels (key: Key, value: Value), so instead
>> of writing `{ tuple in let (key, value) = tuple; f(key, value) }`,
>> you could use the implicit argument and write `{ f($0.key, $0.value)
>> }`.
>> 
>> -Joe
>
> I've migrated a project from Swift 3 to Swift 4 (relevant commit:
> https://github.com/groue/GRDB.swift/commit/4f26cbcacf7b783c9c503f2909f2eb03ef7930fe)
>
> Joe is right, dictionaries, as handy as they are, are particularly affected. 
> But $0 is hardly a
> panacea.
>
> What I regret the most with the change is the lost ability to give
> *relevant names* to tuple elements (and sometimes with the forced
> introduction of a phony variable that has no relevant name (like
> "pair").

Not saying there's no problem here, but `kv` (or `keyValue` if you must)
works pretty well for this.  At least it isn't purely a reflection of
the type and gives some hint as to semantics.

-- 
-Dave

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


[swift-evolution] Pitch: String Index Overhaul

2017-05-27 Thread Dave Abrahams via swift-evolution

Pretty version: 
https://github.com/dabrahams/swift-evolution/blob/string-index-overhaul/proposals/-string-index-overhaul.md



# String Index Overhaul

* Proposal: [SE-](-string-index-overhaul.md)
* Authors: [Dave Abrahams](https://github.com/dabrahams)
* Review Manager: TBD
* Status: **Awaiting review**
* Pull Request Implementing This Proposal: 
https://github.com/apple/swift/pull/9806 

*During the review process, add the following fields as needed:*

## Introduction

Today `String` shares an `Index` type with its `CharacterView` but not
with its `UTF8View`, `UTF16View`, or `UnicodeScalarView`.  This
proposal redefines `String.UTF8View.Index`, `String.UTF16View.Index`,
and `String.CharacterView.Index` as typealiases for `String.Index`,
and exposes a public `encodedOffset` property and initializer that can
be used to serialize and deserialize positions in a `String` or
`Substring`.

Swift-evolution thread: [Discussion thread topic for that 
proposal](https://lists.swift.org/pipermail/swift-evolution/)

## Motivation

The different index types are supported by a set of `Index`
initializers, which are failable whenever the source index might not
correspond to a position in the target view:

```swift
if let j = String.UnicodeScalarView.Index(
  someUTF16Position, within: s.unicodeScalars) {
  ... 
}
```

The current API is as follows:

```swift
public extension String.Index {
  init?(_: String.UnicodeScalarIndex, within: String)
  init?(_: String.UTF16Index, within: String)
  init?(_: String.UTF8Index, within: String)
}

public extension String.UTF16View.Index {
  init?(_: String.UTF8Index, within: String.UTF16View)
  init(_: String.UnicodeScalarIndex, within: String.UTF16View)
  init(_: String.Index, within: String.UTF16View)
}

public extension String.UTF8View.Index {
  init?(_: String.UTF16Index, within: String.UTF8View)
  init(_: String.UnicodeScalarIndex, within: String.UTF8View)
  init(_: String.Index, within: String.UTF8View)
}

public extension String.UnicodeScalarView.Index {
  init?(_: String.UTF16Index, within: String.UnicodeScalarView)
  init?(_: String.UTF8Index, within: String.UnicodeScalarView)
  init(_: String.Index, within: String.UnicodeScalarView)
}
```

These initializers are supplemented by a corresponding set of
convenience conversion methods:

```swift
if let j = someUTF16Position.samePosition(in: s.unicodeScalars) {
  ... 
}
```

with the following API:

```swift
public extension String.Index {
  func samePosition(in: String.UTF8View) -> String.UTF8View.Index
  func samePosition(in: String.UTF16View) -> String.UTF16View.Index
  func samePosition(
in: String.UnicodeScalarView) -> String.UnicodeScalarView.Index
}

public extension String.UTF16View.Index {
  func samePosition(in: String) -> String.Index?
  func samePosition(in: String.UTF8View) -> String.UTF8View.Index?
  func samePosition(
in: String.UnicodeScalarView) -> String.UnicodeScalarView.Index?
}

public extension String.UTF8View.Index {
  func samePosition(in: String) -> String.Index?
  func samePosition(in: String.UTF16View) -> String.UTF16View.Index?
  func samePosition(
in: String.UnicodeScalarView) -> String.UnicodeScalarView.Index?
}

public extension String.UnicodeScalarView.Index {
  func samePosition(in: String) -> String.Index?
  func samePosition(in: String.UTF8View) -> String.UTF8View.Index
  func samePosition(in: String.UTF16View) -> String.UTF16View.Index
}
```

The result is a great deal of API surface area for apparently little
gain in ordinary code, that normally only interchanges indices among
views when the positions match up exactly (i.e. when the conversion is
going to succeed).  Also, the resulting code is needlessly awkward.

Finally, the opacity of these index types makes it difficult to record
`String` or `Substring` positions in files or other archival forms,
and reconstruct the original positions with respect to a deserialized
`String` or `Substring`.

## Proposed solution

All `String` views will use a single index type (`String.Index`), so
that positions can be interchanged without awkward explicit
conversions:

```swift
let html: String = "See http://swift.org\;>swift.org"

// Search the UTF16, instead of characters, for performance reasons:
let open = "<".utf16.first!, close = ">".utf16.first!
let tagStart = s.utf16.index(of: open)
let tagEnd = s.utf16[tagStart...].index(of: close)

// Slice the String with the UTF-16 indices to retrieve the tag.
let tag = html[tagStart...tagEnd]
```

A property and an intializer will be added to `String.Index`, exposing
the offset of the index in code units (currently only UTF-16) from the
beginning of the string:

```swift
let n: Int = html.endIndex.encodedOffset
let end = String.Index(encodedOffset: n)
assert(end == String.endIndex)
```

# Comparison and Slicing Semantics

When two indices being compared correspond to positions that are valid
in any single `String` view, comparison semantics are already fully
specified by the 

Re: [swift-evolution] Feedback on SE-0166 and SE-0167

2017-05-27 Thread David Hart via swift-evolution

> On 27 May 2017, at 12:40, Gwendal Roué  wrote:
> 
> 
>> Le 27 mai 2017 à 11:59, David Hart > > a écrit :
>> 
>> I didn’t know that was possible either! Really cool. Even better:
>> 
>>  if let databaseValueType = T.self as? DatabaseValueConvertible.Type {
>>  let databaseValue: DatabaseValue = row.value(named: key.stringValue)
>>  return databaseValueType.fromDatabaseValue(databaseValue) as! T
>>  } else { … }
> 
> 
> Yes, that's even more clear like that :-)
> 
> And for Encodable support, it works just great (not a single line of 
> boilerplate code):
> 
> private enum Color: String, DatabaseValueConvertible, Encodable {
> case red, green, blue
> }
> 
> private struct EncodableStruct : Persistable, Encodable {
> static let databaseTableName = "myTable"
> let name: String
> let color: Color?
> }
> 
> try dbQueue.inDatabase { db in
> try EncodableStruct(name: "Arthur", color: .red).insert(db)
> }
> 
> Gwendal
> 

As a user of GRDB: ___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Revisiting SE-0110

2017-05-27 Thread Gwendal Roué via swift-evolution
> Le 27 mai 2017 à 14:20, Gwendal Roué  a écrit :
> 
>> Le 26 mai 2017 à 21:35, Robert Bennett via swift-evolution 
>> > a écrit :
>> 
>> On the contrary I think the solution can absolutely be to break other code. 
> 
> You can break whatever you want.
> 
> But whatever the solution you come up with, remember that SE-0110 currently 
> *degrades* the quality of code written by Swift programmers. It can be argued 
> that SE-0110 is a blatant *regression*. Maybe not as bad as the initial 
> introduction of fileprivate, but something which is pretty bad, though.
> 
> References:
> - 
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036808.html
>  
> 
> - 
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036814.html
>  
> 
One more link, about the loss of single-lign closures induced by SE-0110: 
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036792.html

Gwendal

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


Re: [swift-evolution] Revisiting SE-0110

2017-05-27 Thread Gwendal Roué via swift-evolution

> Le 26 mai 2017 à 21:35, Robert Bennett via swift-evolution 
>  a écrit :
> 
> On the contrary I think the solution can absolutely be to break other code. 

You can break whatever you want.

But whatever the solution you come up with, remember that SE-0110 currently 
*degrades* the quality of code written by Swift programmers. It can be argued 
that SE-0110 is a blatant *regression*. Maybe not as bad as the initial 
introduction of fileprivate, but something which is pretty bad, though.

References:
- 
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036808.html
 

- 
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036814.html
 


It would be nice if some responsible language designer would at least *agree* 
that there is a problem.

Gwendal Roué

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


Re: [swift-evolution] Revisiting SE-0110

2017-05-27 Thread Vladimir.S via swift-evolution
Regarding the requiring of parentheses for arguments in closures: I'd suggest to look 
into SE-0066's paragraph "Should we require parentheses in closure expression 
parameter lists?" and "Rationale" for this SE: 
https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000138.html
(SE-0066: 
https://github.com/apple/swift-evolution/blob/master/proposals/0066-standardize-function-type-syntax.md)


Personally I agree that closures worth their own syntax and that it is important to 
keep closure's syntax lightweight. So, IMO we should keep {x, y in ..} syntax for 
closures and this should be jut equivalent for {(x, y) in ..}, i.e. one can use first 
or second depending on his/her style.


I think the idea of double parentheses for tuple argument destructing in closure is 
the best solution as it has a relationship to introduced by SE-0066 type of function 
with tuple argument. I.e.  :


func foo(_ x: (Int, Int)) {..}

type(of: foo) // should be ((Int, Int)) -> ()
// note: not  (Int, Int) -> ()

var g : ((Int, Int))->() = f

g = {((x1, x2)) in ... } // feels like correct syntax for type ((Int, Int))->()
g = {pair in ... } // also OK, single tuple argument

g = {(x1, x2) in ...} // should be ERROR: incompatible type of closure

But during the discussion of SE-0110 there was some negative reaction for this 
idea.

Btw, while we are discussing SE-0110 and SE-0066, I'd like to point to another change 
that can IMO have impact for functional programming, so I believe it is better to 
discuss this also.

Currently(Swift 3.1) we are allowed to have this:

var f : ()->() = {x in print(x)}
f() // ()
f = {_ in print(x)}

I.e. you can now assign a closure with single argument as function type that has 0 
parameters(because of implicit Void argument).


Also, after SE-0066 and 0110 implemented, passing Void to function declared without 
parameters should not be allowed, so for example you can't call f(print("sdfsdf")) 
i.e. can't pass result of Void to some other function that is declared without 
parameters.


Small naive example:

func schedule(with: T, do: (T) -> Void) {...}

//--- can't change this function for some reason ---
func foo() { .. } // just do something
//---

current code:

schedule(with: (), do: foo)

new code:

schedule(with: (), do: {_ in foo()})

Actually I believe this is a good change, but should be taken into 
consideration.

Vladimir.

On 26.05.2017 22:35, Robert Bennett via swift-evolution wrote:
On the contrary I think the solution can absolutely be to break other code. Allowing 
no parentheses is not a very useful shorthand -- it only saves two characters -- and 
with SE-0110 actually obscures the logic. Always requiring parentheses makes it clear 
that "{ (arg) in ..." can be replaced with "{ ((key, value)) in ..." when arg is a 
2-tuple; the outer parentheses denote an argument list, and anything inside them is 
the argument, consistent with other parts of the language. Allowing "{ arg in ..." 
but not "{ (key, value) in ..." is sure to leave some people scratching their heads 
because"{ arg in ..." suggests that it is arguments that are passed to closures (as 
is the case with functions) and not tuples. The correctness of "{ arg in ..." relies 
too heavily on the equivalence between a 1-tuple and a single element, an equivalence 
that does not hold for higher arities.


I'm not *too* strongly wed to this, though. I care much more strongly about 
allowing  "{ ((key, value)) in ..." than prohibiting  "{ arg in ...". I only brought 
up the latter to try to improve the consistency of the language and to make clear 
that  "{ ((key, value)) in ..." is the correct way of achieving the old style "{ 
(key, value) in ..."


On May 26, 2017, at 2:57 PM, Xiaodi Wu > wrote:


Requiring parentheses in closures like that was discussed during SE-0110 and 
rejected; the rationale was that it's a useful shorthand, and that the whole 
closure syntax is wildly different from function declarations: you don't need to 
write the type of the arguments, and you don't need to write the return value, so 
why would it make sense to enforce this particular formality?


At this point, I'd be very much against now requiring it. The whole rationale for 
revisiting SE-0110 is that it is breaking lots of code, and the solution cannot be 
to break lots of other code. The double parenthesis syntax or the let syntax for 
destructuring seem to be fine mitigations, the former because it strictly parallels 
the change in syntax in SE-0110 where a single tuple argument has two sets of 
parentheses, and the latter because it's the same destructuring syntax as is used 
elsewhere for pattern matching.
On Fri, May 26, 2017 at 11:45 David Sweeris via swift-evolution 
> wrote:



> On May 26, 2017, at 08:14, Robert Bennett via swift-evolution
> 

Re: [swift-evolution] Feedback on SE-0166 and SE-0167

2017-05-27 Thread Gwendal Roué via swift-evolution

> Le 27 mai 2017 à 11:59, David Hart  a écrit :
> 
> I didn’t know that was possible either! Really cool. Even better:
> 
>  if let databaseValueType = T.self as? DatabaseValueConvertible.Type {
>  let databaseValue: DatabaseValue = row.value(named: key.stringValue)
>  return databaseValueType.fromDatabaseValue(databaseValue) as! T
>  } else { … }


Yes, that's even more clear like that :-)

And for Encodable support, it works just great (not a single line of 
boilerplate code):

private enum Color: String, DatabaseValueConvertible, Encodable {
case red, green, blue
}

private struct EncodableStruct : Persistable, Encodable {
static let databaseTableName = "myTable"
let name: String
let color: Color?
}

try dbQueue.inDatabase { db in
try EncodableStruct(name: "Arthur", color: .red).insert(db)
}

Gwendal

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


Re: [swift-evolution] Revisiting SE-0110

2017-05-27 Thread Haravikk via swift-evolution

> On 26 May 2017, at 20:35, Robert Bennett via swift-evolution 
>  wrote:
> 
> On the contrary I think the solution can absolutely be to break other code. 
> Allowing no parentheses is not a very useful shorthand -- it only saves two 
> characters -- and with SE-0110 actually obscures the logic. Always requiring 
> parentheses makes it clear that "{ (arg) in ..." can be replaced with "{ 
> ((key, value)) in ..." when arg is a 2-tuple; the outer parentheses denote an 
> argument list, and anything inside them is the argument, consistent with 
> other parts of the language. Allowing "{ arg in ..." but not "{ (key, value) 
> in ..." is sure to leave some people scratching their heads because "{ arg in 
> ..." suggests that it is arguments that are passed to closures (as is the 
> case with functions) and not tuples. The correctness of "{ arg in ..." relies 
> too heavily on the equivalence between a 1-tuple and a single element, an 
> equivalence that does not hold for higher arities.
> 
> I'm not *too* strongly wed to this, though. I care much more strongly about 
> allowing  "{ ((key, value)) in ..." than prohibiting  "{ arg in ...". I only 
> brought up the latter to try to improve the consistency of the language and 
> to make clear that  "{ ((key, value)) in ..." is the correct way of achieving 
> the old style "{ (key, value) in …"

I seem to recall arguing the same with SE-0110, and still hold the same belief; 
requiring the parenthesis avoids a lot of ambiguity when dealing with tuples, 
and it's an easy migration to make (Xcode can just add parenthesis to all 
closures). For code that isn't migrated, syntax errors should crop up naturally 
anywhere the arguments and closure type no longer match.

I still absolutely believe it's the right call; while closures aren't defined 
quite the same as functions, that's no reason not to have *some* consistency. 
There's possibly an argument to make for omitting the brackets on a simple, 
single parameter closure, but other than that I think it's better to be 
explicit in all cases.
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Feedback on SE-0166 and SE-0167

2017-05-27 Thread David Hart via swift-evolution

> On 27 May 2017, at 08:59, Gwendal Roué  wrote:
> 
> 
>> Le 26 mai 2017 à 22:30, David Hart  a écrit :
>> 
>> Can you explain what’s the problem with Issue 2?
> 
> The problem was me, I guess :-) Of course nobody knows the list of keys, but 
> the type itself. It's a matter of injecting an encoder. I'll do that.
> 
> Thanks also Itai for your answer.
> 
>> Am I correct in suggesting that Issue 1 is more of a missing generics 
>> feature than a problem with SE-0166/0167?
> 
> There are two ways to see such issue: either a language is not ready, either 
> a library isn't designed for its language. :-) But this is not the case here. 
> Again, Itai has the correct answer:
> 
>   if T.self is DatabaseValueConvertible.Type {
>   let databaseValue: DatabaseValue = row.value(named: key.stringValue)
>   return (T.self as! 
> DataBaseValueConvertible.Type).fromDatabaseValue(databaseValue) as! T
>   } else { … }

I didn’t know that was possible either! Really cool. Even better:

  if let databaseValueType = T.self as? DatabaseValueConvertible.Type {
  let databaseValue: DatabaseValue = row.value(named: key.stringValue)
  return databaseValueType.fromDatabaseValue(databaseValue) as! T
  } else { … }

> This is the way to test a type against a protocol - I didn't know this was 
> even possible!
> 
> Thanks a lot, Itai and David: SE-0166 and SE-0167 are delivering their 
> promises, and GRDB will make good use from them :-)
> Gwendal
> 
>> 
>> David.
>> 
>>> On 26 May 2017, at 16:26, Gwendal Roué via swift-evolution 
>>>  wrote:
>>> 
>>> Hello,
>>> 
>>> I want to provide real-life feedback for the Swift Archival & Serialization 
>>> (SE-0166) and Swift Encoders (SE-0167) proposals that currently ship in 
>>> Swift 4 snapshots.
>>> 
>>> The context: I'm the author of GRDB.swift [1], a SQLite library that, among 
>>> other goals, aims at easing the conversion between database rows and custom 
>>> models (structs and class hierarchies):
>>> 
>>> // Sample code
>>> let arthur = Player(name: "Arthur", score: 100)
>>> try arthur.insert(db)
>>> print(arthur.id)
>>> 
>>> let topPlayers = try Player
>>> .order(Column("score").desc)
>>> .limit(10)
>>> .fetchAll(db) // [Player]
>>> 
>>> Due to the lack of any introspection in Swift, GRDB currently wants you to 
>>> perform explicit conversion:
>>> 
>>> struct Player {
>>> var id: Int64?
>>> let name: String
>>> let score: Int
>>> }
>>> 
>>> extension Player : RowConvertible {
>>> init(row: Row) {
>>> id = row.value(named: "id")
>>> name = row.value(named: "name")
>>> score = row.value(named: "score")
>>> }
>>> }
>>> 
>>> extension Player : TableMapping, MutablePersistable {
>>> static let databaseTableName = "player"
>>> var persistentDictionary: [String: DatabaseValueConvertible?] {
>>> return ["id": id, "name": name, "score: score]
>>> }
>>> }
>>> 
>>> That's enough, but that's still too much.
>>> 
>>> SE-0166 and SE-0167 sound like the promise that some boilerplate code could 
>>> be automatically generated.
>>> 
>>> Along with JSONDecoder and PListDecoder, let's introduce 
>>> DatabaseRowDecoder! The current state of the work is at 
>>> https://github.com/groue/GRDB.swift/tree/Swift4
>>> 
>>> 
>>> At first, it's very satisfying. Decodable keeps some of it promises:
>>> 
>>> struct Player : RowConvertible, Decodable {
>>> static let databaseTableName = "player"
>>> var id: Int64?
>>> let name: String
>>> let score: Int
>>> }
>>> 
>>> // Yeah, no more extra code necessary for this to work!
>>> let topPlayers = try Player
>>> .order(Column("score").desc)
>>> .limit(10)
>>> .fetchAll(db)
>>> 
>>> But there are some issues.
>>> 
>>> 
>>> ### Issue 1: SE-0166/0167 merge the concepts of keyed objects and values
>>> 
>>> This is a problem. Let's take this example:
>>> 
>>> enum Color: Int, Codable {
>>> case blue, green, red
>>> }
>>> 
>>> struct Flower : RowConvertible, Decodable {
>>> let name: String
>>> let color: Color
>>> }
>>> 
>>> The way to decode a color comes from KeyedDecodingContainerProtocol:
>>> 
>>> protocol KeyedDecodingContainerProtocol {
>>> func decode(_ type: T.Type, forKey key: Key) throws -> T 
>>> where T : Decodable
>>> func decodeIfPresent(_ type: T.Type, forKey key: Key) throws 
>>> -> T? where T : Decodable
>>> }
>>> 
>>> But the ability to decode a Color from a database row comes from the 
>>> DatabaseValueConvertible, which I can't invoke since I can't test if type T 
>>> conforms to this protocol:
>>> 
>>> 

Re: [swift-evolution] Feedback on SE-0166 and SE-0167

2017-05-27 Thread Gwendal Roué via swift-evolution

> Le 26 mai 2017 à 22:30, David Hart  a écrit :
> 
> Can you explain what’s the problem with Issue 2?

The problem was me, I guess :-) Of course nobody knows the list of keys, but 
the type itself. It's a matter of injecting an encoder. I'll do that.

Thanks also Itai for your answer.

> Am I correct in suggesting that Issue 1 is more of a missing generics feature 
> than a problem with SE-0166/0167?

There are two ways to see such issue: either a language is not ready, either a 
library isn't designed for its language. :-) But this is not the case here. 
Again, Itai has the correct answer:

   if T.self is DatabaseValueConvertible.Type {
   let databaseValue: DatabaseValue = row.value(named: key.stringValue)
   return (T.self as! 
DataBaseValueConvertible.Type).fromDatabaseValue(databaseValue) as! T
   } else { ... }

This is the way to test a type against a protocol - I didn't know this was even 
possible!

Thanks a lot, Itai and David: SE-0166 and SE-0167 are delivering their 
promises, and GRDB will make good use from them :-)
Gwendal

> 
> David.
> 
>> On 26 May 2017, at 16:26, Gwendal Roué via swift-evolution 
>>  wrote:
>> 
>> Hello,
>> 
>> I want to provide real-life feedback for the Swift Archival & Serialization 
>> (SE-0166) and Swift Encoders (SE-0167) proposals that currently ship in 
>> Swift 4 snapshots.
>> 
>> The context: I'm the author of GRDB.swift [1], a SQLite library that, among 
>> other goals, aims at easing the conversion between database rows and custom 
>> models (structs and class hierarchies):
>> 
>>  // Sample code
>>  let arthur = Player(name: "Arthur", score: 100)
>>  try arthur.insert(db)
>>  print(arthur.id)
>>  
>>  let topPlayers = try Player
>>  .order(Column("score").desc)
>>  .limit(10)
>>  .fetchAll(db) // [Player]
>> 
>> Due to the lack of any introspection in Swift, GRDB currently wants you to 
>> perform explicit conversion:
>> 
>>  struct Player {
>>  var id: Int64?
>>  let name: String
>>  let score: Int
>>  }
>>  
>>  extension Player : RowConvertible {
>>  init(row: Row) {
>>  id = row.value(named: "id")
>>  name = row.value(named: "name")
>>  score = row.value(named: "score")
>>  }
>>  }
>>  
>>  extension Player : TableMapping, MutablePersistable {
>>  static let databaseTableName = "player"
>>  var persistentDictionary: [String: DatabaseValueConvertible?] {
>>  return ["id": id, "name": name, "score: score]
>>  }
>>  }
>> 
>> That's enough, but that's still too much.
>> 
>> SE-0166 and SE-0167 sound like the promise that some boilerplate code could 
>> be automatically generated.
>> 
>> Along with JSONDecoder and PListDecoder, let's introduce DatabaseRowDecoder! 
>> The current state of the work is at 
>> https://github.com/groue/GRDB.swift/tree/Swift4
>> 
>> 
>> At first, it's very satisfying. Decodable keeps some of it promises:
>> 
>>  struct Player : RowConvertible, Decodable {
>>  static let databaseTableName = "player"
>>  var id: Int64?
>>  let name: String
>>  let score: Int
>>  }
>>  
>>  // Yeah, no more extra code necessary for this to work!
>>  let topPlayers = try Player
>>  .order(Column("score").desc)
>>  .limit(10)
>>  .fetchAll(db)
>> 
>> But there are some issues.
>> 
>> 
>> ### Issue 1: SE-0166/0167 merge the concepts of keyed objects and values
>> 
>> This is a problem. Let's take this example:
>> 
>>  enum Color: Int, Codable {
>>  case blue, green, red
>>  }
>>  
>>  struct Flower : RowConvertible, Decodable {
>>  let name: String
>>  let color: Color
>>  }
>>  
>> The way to decode a color comes from KeyedDecodingContainerProtocol:
>> 
>>  protocol KeyedDecodingContainerProtocol {
>>  func decode(_ type: T.Type, forKey key: Key) throws -> T 
>> where T : Decodable
>>  func decodeIfPresent(_ type: T.Type, forKey key: Key) throws 
>> -> T? where T : Decodable
>>  }
>> 
>> But the ability to decode a Color from a database row comes from the 
>> DatabaseValueConvertible, which I can't invoke since I can't test if type T 
>> conforms to this protocol:
>> 
>>  struct RowKeyedDecodingContainer: 
>> KeyedDecodingContainerProtocol {
>>  let row: Row
>>  
>>  // Not OK: no support for values
>>  func decode(_ type: T.Type, forKey key: Key) throws -> T 
>> where T : Decodable {
>>  if   {
>>  let databaseValue: DatabaseValue = 
>> row.value(named: key.stringValue)
>>  return T.fromDatabaseValue(databaseValue)