[Pitch] Add `mapValues` method to Dictionary

This seems like a great way to do mapValues, as you get the ability to lazily map or flatMap the pairs. I’m pretty sure a key/value pair constructor is coming to Dictionary.

···

On 22 May 2016, at 5:01 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 21 May 2016, at 15:47, Honza Dvorsky <jan.dvorsky@me.com <mailto:jan.dvorsky@me.com>> wrote:

While I agree that it'd be nice to add a Map abstraction into which we could move a lot of the Dictionary-ness, my original pitch is *just* about adding the specific implementation of `mapValues` in its regular, non-lazy form. My example was about only keeping a subset of the information in memory in a Dictionary to allow for quick and frequent access (lazy goes against that). I think it'd be better to get that in first, or at least evaluate that separately from a comprehensive refactoring of the Dictionary, which would just accumulate more opinions and slow this specific step down.

Sorry, my point was that I think it’s better to wait until we can also do the lazy equivalent and have both done together, otherwise we end up with one map function that can work both lazily and one that never does. Sure that will require a refactoring into a protocol, but it seems to me that it’s better to do that as the first step, then add the feature after that. In the mean time extensions have you well covered for convenience.

Another alternative to this feature might be to add a key/value pair constructor to Dictionary (it technically already has one, but it’s variadic only) so you could do something like this:

  let myTransformedDictionary = Dictionary(myIntegerDictionary.lazy.map { ($0, $1 + 5) })

Since this would be a useful initialiser both now and in future. I dunno, it’s just my opinion, but I find it a bit weird to get half the implementation now, and could lead to misunderstandings with people trying to do myMap.lazy.mapValues (won’t be recognised) and wondering why there isn’t one.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Sent from my iPad

I have a small remark though, wouldn’t it be better to let transform be of type (Key, Value) throws -> T instead of (Value) throws -> T? You can just ignore the key (with _) if you don’t need it, but I think it might come in handy in some cases.

The problem is, that closes the door to writing many simple maps in functional style. For instance, this:

  dictionaryOfNumbers.mapValues(abs)

Would have to become this:

  dictionaryOfNumbers.mapValues { _, v in abs(v) }

(It *might* be possible to do it with `$1`, but I'm not sure; there are some limitations around that.)

A value-value map is just simpler and cleaner, while almost always giving you what you need.

+1.

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

I think this conversation suffers from not knowing whether Dictionary is going to receive a sequence-based initializer. There's been a proposal for that in the PR queue for quite a while. Could someone in the core team please either merge or close that proposal?

If there's no sequence-based init, mapValues should probably produce a new Dictionary. If there is, having mapValues produce a dictionary would be redundant. I'd rather see a MapDictionary type that wraps a dictionary and only evaluates the transformation on access, rather than eagerly allocating storage and performing all the transformations.

You can take a look at a rudimentary MapDictionary in this gist: MapDictionary.swift · GitHub

Best,
Nate

···

On May 24, 2016, at 7:43 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

As has been pointed out in other messages here, the slight change I suggested would let you see the key when mapping the value, but it wouldn’t let you transform the key. Either way, if Swift 3 gives us a dictionary initialiser from a sequence, then this change won’t be necessary. If it won’t, I still think seeing the key could come in handy at times (although I can’t think of an example).

···

On 24 May 2016, at 14:43, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I have a small remark though, wouldn’t it be better to let transform be of type (Key, Value) throws -> T instead of (Value) throws -> T? You can just ignore the key (with _) if you don’t need it, but I think it might come in handy in some cases.

The problem is, that closes the door to writing many simple maps in functional style. For instance, this:

  dictionaryOfNumbers.mapValues(abs)

Would have to become this:

  dictionaryOfNumbers.mapValues { _, v in abs(v) }

(It *might* be possible to do it with `$1`, but I'm not sure; there are some limitations around that.)

A value-value map is just simpler and cleaner, while almost always giving you what you need.

+1.

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

--
Brent Royal-Gordon
Architechies

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

That's the part that I don't know, would encourage anyone who has a link of
that being said (or anyone from the core team) to step in here. Obviously
if it's required to have a lazy variant for all collection methods, I'd
probably pull back this proposal, as it would automatically be rejected by
the core team. (But I hope it's not the case, as that means there's a
pretty high barrier of entry for adding more convenience methods to the
stdlib).

···

On Sat, May 21, 2016 at 9:11 PM Dan Appel <dan.appel00@gmail.com> wrote:

Correct me if I'm wrong, but isn't part of the goal of lazy collections to
be able to prefix a whole chain of operations with .lazy and have it still
compile (and be more efficient)? If so, then the core team would probably
want both variants in the standard library.
On Sat, May 21, 2016 at 12:16 PM Honza Dvorsky via swift-evolution < > swift-evolution@swift.org> wrote:

I see your point, I agree it would definitely be nicer to have both
variants, but is that a rule that *all* methods on Collection and
Dictionary etc also have lazy variants? I actually don't know (please tell
me if you do).

The question here is not really whether to add a lazy variant later (I
don't think anyone would object to that), but as that would require quite
substantial refactoring and abstraction change into a Map protocol (which
will definitely gather a lot of feedback and could drag on for months), I
don't see why we can't add the regular variant already and benefit from it
now. Another option is that there won't be demand for the lazy variant, in
which case it makes even less sense to block the regular variant right now
(and could even hurt this proposal).

Maybe I'm missing some information about the core team requiring always
adding both non-lazy and lazy variants, if so, please do tell me so that I
can re-evaluate my approach. If not, I'd like to keep the thread focused
strictly on the one method I'm proposing we add, for the benefits and
examples I provided.

The Dictionary initializers could slightly help things, but it'd require
nesting if you do more than one level, whereas `mapValues` would allow for
multiple transformations to be applied in sequence without additional
nesting (my example from the original pitch would turn into `var
descriptionTextLengths = Dictionary(Dictionary(repos.mapValues {
$0["description"].string }).mapValues { $0.characters.count })` which is
much uglier in my opinion (and kind of breaks the natural left-to-right
composition).

On Sat, May 21, 2016 at 8:01 PM Haravikk <swift-evolution@haravikk.me> >> wrote:

On 21 May 2016, at 15:47, Honza Dvorsky <jan.dvorsky@me.com> wrote:

While I agree that it'd be nice to add a Map abstraction into which we
could move a lot of the Dictionary-ness, my original pitch is *just* about
adding the specific implementation of `mapValues` in its regular, non-lazy
form. My example was about only keeping a subset of the information in
memory in a Dictionary to allow for quick and frequent access (lazy goes
against that). I think it'd be better to get that in first, or at least
evaluate that separately from a comprehensive refactoring of the
Dictionary, which would just accumulate more opinions and slow this
specific step down.

Sorry, my point was that I think it’s better to wait until we can also
do the lazy equivalent and have both done together, otherwise we end up
with one map function that can work both lazily and one that never does.
Sure that will require a refactoring into a protocol, but it seems to me
that it’s better to do that as the first step, then add the feature after
that. In the mean time extensions have you well covered for convenience.

Another alternative to this feature might be to add a key/value pair
constructor to Dictionary (it technically already has one, but it’s
variadic only) so you could do something like this:

let myTransformedDictionary = Dictionary(myIntegerDictionary.lazy.map {
($0, $1 + 5) })

Since this would be a useful initialiser both now and in future. I
dunno, it’s just my opinion, but I find it a bit weird to get half the
implementation now, and could lead to misunderstandings with people trying
to do myMap.lazy.mapValues (won’t be recognised) and wondering why there
isn’t one.

_______________________________________________

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

--
Dan Appel

Sent from my iPad

I have a small remark though, wouldn’t it be better to let transform be of type (Key, Value) throws -> T instead of (Value) throws -> T? You can just ignore the key (with _) if you don’t need it, but I think it might come in handy in some cases.

The problem is, that closes the door to writing many simple maps in functional style. For instance, this:

dictionaryOfNumbers.mapValues(abs)

Would have to become this:

dictionaryOfNumbers.mapValues { _, v in abs(v) }

(It *might* be possible to do it with `$1`, but I'm not sure; there are some limitations around that.)

A value-value map is just simpler and cleaner, while almost always giving you what you need.

+1.

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

I think this conversation suffers from not knowing whether Dictionary is going to receive a sequence-based initializer. There's been a proposal for that in the PR queue for quite a while. Could someone in the core team please either merge or close that proposal?

Add sequence-based initializers and merge methods to Dictionary by natecook1000 · Pull Request #125 · apple/swift-evolution · GitHub

If there's no sequence-based init, mapValues should probably produce a new Dictionary. If there is, having mapValues produce a dictionary would be redundant. I'd rather see a MapDictionary type that wraps a dictionary and only evaluates the transformation on access, rather than eagerly allocating storage and performing all the transformations.

The eager vs lazy issue has already been discussed. Why not support both and let the user decide?

The best approach to laziness is to introduce a Map protocol that allows us to have lazy maps of various kinds, as we do with sequences and protocols. That seems out of scope for Swift 3. But there is no reason it should delay an eager method.

···

Sent from my iPad

On May 24, 2016, at 11:34 AM, Nate Cook <nate@natecook.com> wrote:

On May 24, 2016, at 7:43 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

You can take a look at a rudimentary MapDictionary in this gist: MapDictionary.swift · GitHub

Best,
Nate

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

Nate Cook via swift-evolution <swift-evolution@...> writes:

>
> Sent from my iPad
>
>
>>> I have a small remark though, wouldn’t it be better to let transform

be of type (Key, Value) throws -> T

instead of (Value) throws -> T? You can just ignore the key (with _) if

you don’t need it, but I think it

might come in handy in some cases.
>>
>> The problem is, that closes the door to writing many simple maps in

functional style. For instance, this:

>>
>> dictionaryOfNumbers.mapValues(abs)
>>
>> Would have to become this:
>>
>> dictionaryOfNumbers.mapValues { _, v in abs(v) }
>>
>> (It *might* be possible to do it with `$1`, but I'm not sure; there are

some limitations around that.)

>>
>> A value-value map is just simpler and cleaner, while almost always

giving you what you need.

>
> +1.
>
> I don't think I have ever mapped keys. Incidentally, that doesn't have

the usual semantics of a map

operation as you can produce duplicate keys.

I think this conversation suffers from not knowing whether Dictionary is

going to receive a

sequence-based initializer. There's been a proposal for that in the PR

queue for quite a while. Could

someone in the core team please either merge or close that proposal?

Add sequence-based initializers and merge methods to Dictionary by natecook1000 · Pull Request #125 · apple/swift-evolution · GitHub

+1 for scheduling that proposal!

If there's no sequence-based init, mapValues should probably produce a new

Dictionary. If there is,

having mapValues produce a dictionary would be redundant. I'd rather see a

MapDictionary type that wraps

a dictionary and only evaluates the transformation on access, rather than

eagerly allocating storage

and performing all the transformations.

I don't think it would be redundant, because producing a new Dictionary from
a Sequence should have complexity O(N log N) if I'm not mistaken, while in
principle mapValues() could be implemented more efficiently, producing a new
Dictionary with the same structure and different values with just O(N)
complexity.

Nicola

You can take a look at a rudimentary MapDictionary in this gist:

···

> On May 24, 2016, at 7:43 AM, Matthew Johnson via swift-evolution <swift-evolution <at> swift.org> wrote:
> On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution <at> swift.org> wrote:

Best,
Nate

>> --
>> Brent Royal-Gordon
>> Architechies
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution <at> swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution <at> swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution <at> swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Sent from my iPad

I have a small remark though, wouldn’t it be better to let transform be of type (Key, Value) throws -> T instead of (Value) throws -> T? You can just ignore the key (with _) if you don’t need it, but I think it might come in handy in some cases.

The problem is, that closes the door to writing many simple maps in functional style. For instance, this:

  dictionaryOfNumbers.mapValues(abs)

Would have to become this:

  dictionaryOfNumbers.mapValues { _, v in abs(v) }

(It *might* be possible to do it with `$1`, but I'm not sure; there are some limitations around that.)

A value-value map is just simpler and cleaner, while almost always giving you what you need.

+1.

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

I think this conversation suffers from not knowing whether Dictionary
is going to receive a sequence-based initializer. There's been a
proposal for that in the PR queue for quite a while. Could someone in
the core team please either merge or close that proposal?

Add sequence-based initializers and merge methods to Dictionary by natecook1000 · Pull Request #125 · apple/swift-evolution · GitHub

Done. I was just waiting until we could assign it SE-0100.
Congratulations! ;-)

···

on Tue May 24 2016, Nate Cook <swift-evolution@swift.org> wrote:

On May 24, 2016, at 7:43 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If there's no sequence-based init, mapValues should probably produce a
new Dictionary. If there is, having mapValues produce a dictionary
would be redundant. I'd rather see a MapDictionary type that wraps a
dictionary and only evaluates the transformation on access, rather
than eagerly allocating storage and performing all the
transformations.

You can take a look at a rudimentary MapDictionary in this gist:
MapDictionary.swift · GitHub

Best,
Nate

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

--
Dave

Obviously if it's required to have a lazy variant for all collection methods, I'd probably pull back this proposal, as it would automatically be rejected by the core team.

I'm not a member of the core team, but I doubt it's necessary.

I don't think it makes much sense to wait for a lazy implementation. If there *is* going to be a Map protocol, then the best way to implement it will require an advanced generics feature that's still in the proposal stages:

  // Note: I'm simplifying signatures here.

  protocol Map: Collection {
    associatedtype Key
    associatedtype Value
    associatedtype Iterator: IteratorProtocol where Iterator.Element == (Key, Value)
    
    subscript (key: Key) -> Value? { get }
    func mapValues<T>(transform: Value -> T) -> [Key: T]
  }

And implementing the laziness will similarly work best with not only that feature, but also conditional conformances:
  
  protocol LazyMap: Map, LazyCollectionProtocol {
    subscript (key: Base.Key) -> Base.Value? { get }
    func mapValues<T>(transform: Value -> T)(…) -> LazyMappedValuesMap<Base, T>
  }

  extension LazyCollection: LazyMap where Base: Map {
    …
  }
  
  struct LazyMapValuesMap<Base: Map, NewValue>: Map {
    …
  }

If we *don't* wait, on the other hand, we're going to end up in manual-specification and GYB hell. (And even then, I believe conditional conformances are not enough to force all LazyCollectionProtocol conformers to support Map if they have a Map base. You'd need conditional *conformance requirements*, which are *not* planned because it's not safe to add a requirement to a protocol.)

I just don't think laziness is so crucial that we should stop the presses until we have it. `mapValues` carries water all by itself, even without a matching `lazy.mapValues`.

···

--
Brent Royal-Gordon
Architechies

Sent from my iPad

I have a small remark though, wouldn’t it be better to let transform be of type (Key, Value) throws -> T instead of (Value) throws -> T? You can just ignore the key (with _) if you don’t need it, but I think it might come in handy in some cases.

The problem is, that closes the door to writing many simple maps in functional style. For instance, this:

dictionaryOfNumbers.mapValues(abs)

Would have to become this:

dictionaryOfNumbers.mapValues { _, v in abs(v) }

(It *might* be possible to do it with `$1`, but I'm not sure; there are some limitations around that.)

A value-value map is just simpler and cleaner, while almost always giving you what you need.

+1.

I don't think I have ever mapped keys. Incidentally, that doesn't have the usual semantics of a map operation as you can produce duplicate keys.

I think this conversation suffers from not knowing whether Dictionary
is going to receive a sequence-based initializer. There's been a
proposal for that in the PR queue for quite a while. Could someone in
the core team please either merge or close that proposal?

Add sequence-based initializers and merge methods to Dictionary by natecook1000 · Pull Request #125 · apple/swift-evolution · GitHub

Done. I was just waiting until we could assign it SE-0100.
Congratulations! ;-)

:100::joy:

···

On May 26, 2016, at 4:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Tue May 24 2016, Nate Cook <swift-evolution@swift.org> wrote:

On May 24, 2016, at 7:43 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 24, 2016, at 12:59 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If there's no sequence-based init, mapValues should probably produce a
new Dictionary. If there is, having mapValues produce a dictionary
would be redundant. I'd rather see a MapDictionary type that wraps a
dictionary and only evaluates the transformation on access, rather
than eagerly allocating storage and performing all the
transformations.

You can take a look at a rudimentary MapDictionary in this gist:
MapDictionary.swift · GitHub

Best,
Nate

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

--
Dave

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

I don't understand - why do we need a map protocol for a lazy mapvalues?

···

On Sat, May 21, 2016 at 11:12 PM Brent Royal-Gordon <brent@architechies.com> wrote:

> Obviously if it's required to have a lazy variant for all collection
methods, I'd probably pull back this proposal, as it would automatically be
rejected by the core team.

I'm not a member of the core team, but I doubt it's necessary.

I don't think it makes much sense to wait for a lazy implementation. If
there *is* going to be a Map protocol, then the best way to implement it
will require an advanced generics feature that's still in the proposal
stages:

        // Note: I'm simplifying signatures here.

        protocol Map: Collection {
                associatedtype Key
                associatedtype Value
                associatedtype Iterator: IteratorProtocol where
Iterator.Element == (Key, Value)

                subscript (key: Key) -> Value? { get }
                func mapValues<T>(transform: Value -> T) -> [Key: T]
        }

And implementing the laziness will similarly work best with not only that
feature, but also conditional conformances:

        protocol LazyMap: Map, LazyCollectionProtocol {
                subscript (key: Base.Key) -> Base.Value? { get }
                func mapValues<T>(transform: Value -> T)(…) ->
LazyMappedValuesMap<Base, T>
        }

        extension LazyCollection: LazyMap where Base: Map {
                …
        }

        struct LazyMapValuesMap<Base: Map, NewValue>: Map {
                …
        }

If we *don't* wait, on the other hand, we're going to end up in
manual-specification and GYB hell. (And even then, I believe conditional
conformances are not enough to force all LazyCollectionProtocol conformers
to support Map if they have a Map base. You'd need conditional *conformance
requirements*, which are *not* planned because it's not safe to add a
requirement to a protocol.)

I just don't think laziness is so crucial that we should stop the presses
until we have it. `mapValues` carries water all by itself, even without a
matching `lazy.mapValues`.

--
Brent Royal-Gordon
Architechies

--

Dan Appel

Obviously if it's required to have a lazy variant for all collection methods, I'd probably pull back this proposal, as it would automatically be rejected by the core team.

I'm not a member of the core team, but I doubt it's necessary.

I don't think it makes much sense to wait for a lazy implementation. If there *is* going to be a Map protocol, then the best way to implement it will require an advanced generics feature that's still in the proposal stages:

   // Note: I'm simplifying signatures here.

   protocol Map: Collection {
       associatedtype Key
       associatedtype Value
       associatedtype Iterator: IteratorProtocol where Iterator.Element == (Key, Value)
       
       subscript (key: Key) -> Value? { get }
       func mapValues<T>(transform: Value -> T) -> [Key: T]
   }

Map should not refine Collection. We could have MapCollection (or KeyValueCollection) that refines both.

protocol Map {
       associatedtype Key
       associatedtype Value
       subscript (key: Key) -> Value { get }
}

protocol MutableMap {
       associatedtype Key
       associatedtype Value
       subscript (key: Key) -> Value { get set }
}

There are plenty of times where you need to be able to read from (and sometimes write to) a map without needing to know anything about how the map is implemented. A single argument function should be a valid Map (I know functions cannot conform to protocols, but I argue that they should have this ability eventually, at least protocols that only have subscript requirements).

Also note that Value *is not* Optional. There are valid maps that don't return Optional. For example, you can implement a Dictionary that takes a default value in its initializer. The function example I just gave is another example. Because Dictionary already defines a Value generic argument and returns 'Value?' From the subscript we will need to either use something clunky like MapValue (which would be 'Value?' for Dictionary) or make a change in Dictionary. But the Map protocol *should not* make the assumption that all maps are partial.

···

Sent from my iPad
On May 22, 2016, at 1:12 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

And implementing the laziness will similarly work best with not only that feature, but also conditional conformances:
   
   protocol LazyMap: Map, LazyCollectionProtocol {
       subscript (key: Base.Key) -> Base.Value? { get }
       func mapValues<T>(transform: Value -> T)(…) -> LazyMappedValuesMap<Base, T>
   }

   extension LazyCollection: LazyMap where Base: Map {
       …
   }
   
   struct LazyMapValuesMap<Base: Map, NewValue>: Map {
       …
   }

If we *don't* wait, on the other hand, we're going to end up in manual-specification and GYB hell. (And even then, I believe conditional conformances are not enough to force all LazyCollectionProtocol conformers to support Map if they have a Map base. You'd need conditional *conformance requirements*, which are *not* planned because it's not safe to add a requirement to a protocol.)

I just don't think laziness is so crucial that we should stop the presses until we have it. `mapValues` carries water all by itself, even without a matching `lazy.mapValues`.

--
Brent Royal-Gordon
Architechies

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

Please correct me if I'm off here, but I quickly looked at the
implementation of lazy collections/sequences, and I think it's because the
goal is to keep the API of e.g. normal Array and a lazy Array the same, so
that you don't actually have to know which one you're working with. But
under the hood, they're very different objects, as one owns the memory of
its elements, whereas the lazy one goes back to the array it was created
with and pulls it elements (before e.g. transforming them). And as structs
cannot be subclassed, a lot of the functionality is implemented in default
functions on the protocol. So if you wanted to create a lazy version of a
Dictionary, which has an identical API, you'd have to duplicate a lot of
code - unless you pull the Dictionary-ness into a protocol, make Dictionary
just the normal form of Dictionary, but allow a LazyDictionary under the
hood to pull values from a parent dictionary based on keys, transform them
with mapValues and return them.

Does that make sense? I looked at the implementation of Dictionary in the
stdlib yesterday, and IMO it'd be a pretty huge amount of work to refactor
it into the Map protocol first, so I'm trying to push for having a non-lazy
version done first, and then add the lazy version once someone proposes to
add the Map protocol later (otherwise this'll just get stuck here for
months, because I doubt the core team would be happy with making such large
changes for allowing one convenience method only, now that Swift 3 is being
stabilized).

···

On Sun, May 22, 2016 at 7:48 AM Dan Appel <dan.appel00@gmail.com> wrote:

I don't understand - why do we need a map protocol for a lazy mapvalues?
On Sat, May 21, 2016 at 11:12 PM Brent Royal-Gordon < > brent@architechies.com> wrote:

> Obviously if it's required to have a lazy variant for all collection
methods, I'd probably pull back this proposal, as it would automatically be
rejected by the core team.

I'm not a member of the core team, but I doubt it's necessary.

I don't think it makes much sense to wait for a lazy implementation. If
there *is* going to be a Map protocol, then the best way to implement it
will require an advanced generics feature that's still in the proposal
stages:

        // Note: I'm simplifying signatures here.

        protocol Map: Collection {
                associatedtype Key
                associatedtype Value
                associatedtype Iterator: IteratorProtocol where
Iterator.Element == (Key, Value)

                subscript (key: Key) -> Value? { get }
                func mapValues<T>(transform: Value -> T) -> [Key: T]
        }

And implementing the laziness will similarly work best with not only that
feature, but also conditional conformances:

        protocol LazyMap: Map, LazyCollectionProtocol {
                subscript (key: Base.Key) -> Base.Value? { get }
                func mapValues<T>(transform: Value -> T)(…) ->
LazyMappedValuesMap<Base, T>
        }

        extension LazyCollection: LazyMap where Base: Map {
                …
        }

        struct LazyMapValuesMap<Base: Map, NewValue>: Map {
                …
        }

If we *don't* wait, on the other hand, we're going to end up in
manual-specification and GYB hell. (And even then, I believe conditional
conformances are not enough to force all LazyCollectionProtocol conformers
to support Map if they have a Map base. You'd need conditional *conformance
requirements*, which are *not* planned because it's not safe to add a
requirement to a protocol.)

I just don't think laziness is so crucial that we should stop the presses
until we have it. `mapValues` carries water all by itself, even without a
matching `lazy.mapValues`.

--
Brent Royal-Gordon
Architechies

--

Dan Appel