Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed API breaking changes #2

Open
cfilipov opened this issue Mar 31, 2017 · 0 comments
Open

Proposed API breaking changes #2

cfilipov opened this issue Mar 31, 2017 · 0 comments

Comments

@cfilipov
Copy link
Owner

cfilipov commented Mar 31, 2017

This proposal supersedes #1, which was tentatively abandoned. This proposal is a breaking API change predicated on the approval of improved key paths in swift SE-0161.

New Text Table Creation

I propose a new API for constructing a TextTable instance. In particular, the way column definitions are mapped to values. Column/value mapping will be defined via a dictionary of key paths to columns.

Current API

The current API uses an initializer that takes a closure which is expected to return an array of column definitions given an instance of some row type Person.

let table = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age),
     Column("Birthday" <- $0.birthday)]
}

This has a few disadvantages:

  1. We can't get the column definition without a value passed to the closure, so a dummy value (the instance for the 0th row) is passed to get the column names.
  2. When computing column widths we actually want to traverse all the values of one column at a time, but we are forced to get the whole row each time.
  3. The column definition itself loses the type information of the value, storing it in an Any.

New API

With the new API, TextTable will conform to ExpressibleByDictionaryLiteral. A TextTable<T> will be created by using a dictionary literal of [PartialKeyPath<T>: Column].

Questions

  1. Should Column be Column<T>?
  2. Could/should we use KeyPath<T, V> instead?
let table: TextTable<Person> = [
    .name: "Name",
    .country: "Country",
    .visited: "Last Visit"
]

The above snippet of code is just short hand for the following (Column will conform to ExpressibleByStringLiteral, so Column does not have to be explicitly constructed):

let table: TextTable<Person> = [
    .name: Column("Name"),
    .country: Column("Country"),
    .visited: Column("Last Visit")
]

The above snippet is also short hand for the following code. This example illustrates the default values that are used when you leave out the optional arguments. Typically you would only provide arguments where you want to customize the specific column.

let table: TextTable<Person> = [
    .name: Column("Name", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    .country: Column("Country", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    .visited: Column("Last Visit", width: .auto, align: .auto, truncate: .tail, formatter: nil)
]

You would never type this out, but for illustration purposes here is an even more explicit version. Once again, this is equivalent to all the previous examples:

let table: TextTable<Person> = [
    #keyPath(Person, .name): Column("Name", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    #keyPath(Person, .country): Column("Country", width: .auto, align: .auto, truncate: .tail, formatter: nil),
    #keyPath(Person, .visited): Column("Last Visit", width: .auto, align: .auto, truncate: .tail, formatter: nil)
]

String Constructor Instead of String Method

Another change to consider: Extending String with an init for TextTable instead of using a string(for:) method.

let s = String(table: table)

Instead of

let s = table.string(for: data)

Stream Print

TextTable tries to be memory efficient. It will not copy the contents of your collection, instead it will call back to your collection whenever it needs a value.

Currently the print method on table just creates a string using the aforementioned API and just passes it to Swift.print. The next version will instead stream the contents of the table to Swift.print roughly line-by-line as the table is generated.

Conformance to Sequence Type

TextTable will actually conform to swift's Collection (or maybe just provide a Sequence). This will let one lazily pull the string parts of the rendered table.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant