Great! And FWIW I thought the same thing, the Map abstraction might be a too large of a task at this time (but I'll definitely support it if it eventually comes up).
On Sat, May 21, 2016 at 3:54 PM Matthew Johnson <[email protected]> wrote: > > > Sent from my iPad > > On May 21, 2016, at 9:47 AM, Honza Dvorsky <[email protected]> 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. > > If one of you have specific ideas about the potential Map protocol, I > encourage you to start a separate thread for that, to focus the > conversation on the parameters of what it would look like. > > I guess I'm now asking - would you support a proposal for adding the basic > mapValues function as the first step, with the potential extendability to a > Map protocol allowing for a lazy version? Because I'd like to keep the > proposal as focused as possible to increase the chance of an on-point > discussion. > > > Yeah, sorry for the digression. I would support it. I don't think we'll > see the Map abstraction is Swift 3 and it would be very useful on its own. > > > Thanks, > Honza > > > On Sat, May 21, 2016 at 3:27 PM Matthew Johnson <[email protected]> > wrote: > >> >> >> Sent from my iPad >> >> > On May 21, 2016, at 8:45 AM, Haravikk via swift-evolution < >> [email protected]> wrote: >> > >> > I think that before this can be done there needs to be an abstraction >> of what a Dictionary is, for example a Map<Key, Value> protocol. This would >> allow us to also implement the important lazy variations of what you >> suggest, which would likely be more important for very large dictionaries >> as dictionaries are rarely consumed in their entirety; in other words, >> calculating and storing the transformed value for every key/value pair is >> quite a performance overhead if only a fraction of those keys may actually >> be accessed. Even if you are consuming the whole transformed dictionary the >> lazy version is better since it doesn’t store any intermediate values, you >> only really want a fully transformed dictionary if you know the >> transformation is either very costly, or transformed values will be >> accessed frequently. >> > >> > Anyway, long way of saying that while the specific implementation is >> definitely wanted, the complete solution requires a few extra steps which >> should be done too, as lazy computation can have big performance benefits. >> > >> > That and it’d be nice to have a Map protocol in stdlib for defining >> other map types, such as trees, since these don’t require Hashable keys, >> but dictionaries do. >> >> +1 to defining map abstractions in the standard library (separating read >> only from read write). The value associatedtype should not take a position >> on optionality, allowing for maps which have a valid value for all possible >> keys. I have done similar things in other languages and found it extremely >> useful. It is not uncommon to have code that just needs to read and / or >> write to / from a map without having concern for the implementation of the >> map. >> >> One issue I think we should sort out along side this is some kind of >> abstraction which allows code to use functions or user-defined types >> without regard for which it is accessing. The map abstraction would build >> on this abstraction, allowing single argument functions to be viewed as a >> read only map. >> >> One option is to allow functions to conform to protocols that only have >> subscript { get } requirements (we would probably only allow them to be >> subscripted through the protocol interface). I think this feels like the >> most Swifty direction. >> >> Another option is to take the path I have seen in several languages which >> is to allow overloading of the function call "operator". I originally >> wanted this in Swift but now wonder if the first option might be a better >> way to accomplish the same goals. >> >> -Matthew >> >> > >> >> On 21 May 2016, at 11:27, Honza Dvorsky via swift-evolution < >> [email protected]> wrote: >> >> >> >> Hello everyone, >> >> >> >> I have added a very simple, but powerful method into a Dictionary >> extension on multiple projects in the last weeks, so I'd like to bring up >> the idea of adding it into the standard library, in case other people can >> see its benefits as well. >> >> >> >> Currently, Dictionary conforms to Collection with its Element being >> the tuple of Key and Value. Thus transforming the Dictionary with regular >> map results in [T], whereas I'd find it more useful to also have a method >> which results in [Key:T]. >> >> >> >> Let me present an example of where this makes sense. >> >> >> >> I recently used the GitHub API to crawl some information about >> repositories. I started with just names (e.g. "/apple/swift", >> "/apple/llvm") and fetched a JSON response for each of the repos, each >> returning a dictionary, which got saved into one large dictionary as the >> end of the full operation, keyed by its name, so the structure was >> something like >> >> >> >> { >> >> "/apple/swift": { "url":..., "size":...., "homepage":... }, >> >> "/apple/llvm": { "url":..., "size":...., "homepage":... }, >> >> ... >> >> } >> >> >> >> To perform analysis, I just needed a dictionary mapping the name of >> the repository to its size, freeing me to discard the rest of the results. >> >> This is where things get interesting, because you can't keep this >> action nicely functional anymore. I had to do the following: >> >> >> >> let repos: [String: JSON] = ... >> >> var sizes: [String: Int] = [:] >> >> for (key, value) in repos { >> >> sizes[key] = value["size"].int >> >> } >> >> // use sizes... >> >> >> >> Which isn't a huge amount of work, but it creates unnecessary mutable >> state in your transformation pipeline (and your current scope). And I had >> to write it enough times to justify bringing it up on this list. >> >> >> >> I suggest we add the following method to Dictionary: >> >> >> >> extension Dictionary { >> >> public func mapValues<T>(_ transform: @noescape (Value) throws -> >> T) rethrows -> [Key: T] { >> >> var transformed: [Key: T] = [:] >> >> for (key, value) in self { >> >> transformed[key] = try transform(value) >> >> } >> >> return transformed >> >> } >> >> } >> >> >> >> It is modeled after Collection's `map` function, with the difference >> that >> >> a) only values are transformed, instead of the Key,Value tuple and >> >> b) the returned structure is a transformed Dictionary [Key:T], instead >> of [T] >> >> >> >> This now allows a much nicer workflow: >> >> >> >> let repos: [String: JSON] = ... >> >> var sizes = repos.mapValues { $0["size"].int } >> >> // use sizes... >> >> >> >> and even multi-step transformations on Dictionaries, previously only >> possible on Arrays, e.g. >> >> var descriptionTextLengths = repos.mapValues { >> $0["description"].string }.mapValues { $0.characters.count } >> >> >> >> You get the idea. >> >> >> >> What do you think? I welcome all feedback, I'd like to see if people >> would support it before I write a proper proposal. >> >> >> >> Thanks! :) >> >> Honza Dvorsky >> >> >> >> _______________________________________________ >> >> swift-evolution mailing list >> >> [email protected] >> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> > >> > _______________________________________________ >> > swift-evolution mailing list >> > [email protected] >> > https://lists.swift.org/mailman/listinfo/swift-evolution >> >>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
