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
