Map on dictionary

I just came across this expression in our codebase:

someDictionary.map { $0 }

and I was thinking "why is this necessary? map is an method on functors, and by the functor laws, functor.map { $0 } == functor should always be true".

It turns out that this converts a dictionary into an array of key-value-pairs, something which I think is highly non-obvious (even if one doesn't care about functor laws).

Maybe I'm the wrong audience here, but I'm wondering if there is some more fundamental rationale behind this - maybe this has been discussed elsewhere before.

1 Like

How I see this is that Dictionary doesn't have a map, it inherits Collection (the protocol)'s map.
Collection has (I believe) a perfectly functor-ially sensible map, a Collection of Element combined with an (Element) -> T function resulting in a Collection of T.
The fact that Dictionary is map-able comes down to protocol conformance: A Collection is map-able and a Dictionary is a Collection therefore Dictionary is map-able.

Hm, that would make sense. For map to really be "functorially sensible", though, it would require that it maps one collection to the same type of collection, at least that's what I would think.

I wonder how this fits in with the (still very early-stage) discussion about adding HKT to Swift.

2 Likes

This has come up before: Provide map and flatMap for Set<A> that return a Set. Yes, higher-kinded types would solve this problem, if it was something that we wanted.

1 Like

Even if (hopefully when) Swift gets higher-kindred types it would be difficult to fix the issue with map.

It would be a massively breaking change to change the signature. A new overload on Collection would introduce unwanted ambiguity and we may not want to require all collections to be functors anyway. Even if we did, the proper functorial map for Dictionary is currently called mapValues which is not a map over its Element of (Key, Value).

Given the lack of core team interest in supporting functional programming abstractions directly I don’t expect to see a solution to this happen in the standard library even if the necessary language features arrive someday. I wish I were more optimistic about this but I would rather be pleasantly surprised if it happens than disappointed if it doesn’t.

Good point. It would always be possible to go the Haskell way, though, and introduce something like fmap (or maybe functorMap?) later on.

Interesting. Can you elaborate a bit more? I was under the impression that Swift encourages functional programming (even if it doesn't force you into it) and that HKT are at least on the radar (I can understand them not being a priority though).

Yeah, we would have to do that. I find this really unfortunate even though fmap would match other languages.

I’m speaking specifically of FP abstractions here, starting with Functor, Applicative and Monad. Some of the core team have explicitly expressed skepticism that they should exist in Swift as standardized abstractions. I think part of the reason for this is the direct language support for Optional and throws and perhaps others in the future.

I think the best way to change this would be to come up with compelling examples that would be considered idiomatic Swift that are only possible when using these abstractions. The good news is that it is possible to encode HKT in Swift as it exists now so anyone interested in doing so can work with them and try to come up with that examples.

Hm, I guess this is the beginner vs. expert tradeoff, which I can understand is a very delicate balance. I wonder, though, if it wouldn't be possible to make optional chaining and throws become syntactic sugar for something much more general at some point...

Although Functor etc. not existing in the stdlib doesn't necessarily preclude other libraries adding them and adapting the stdlib to use them, once HKT are available. As long as there was such a library and it was well supported, I would find it acceptable if those weren't in the core language.

There was a pitch around doing this for optional chaining recently. There has also been a little bit of discussion of maybe introducing user-defined effects in the future. That would be a generalization of throws.

Sure, this is what happened in Scala and Kotlin. We can't reclaim the name map from the standard library but as you pointed out, we can always use fmap instead. IMO the name fmap feels out of place in Swift but we might have to live with it.

As I mentioned, there is nothing stopping you from writing such a library today if you're willing to use the encoding of HKT. Arrow has gotten very far with that approach in Kotlin. I think it's only a matter of time before something emerges in Swift. I would like to work on an FP library myself but don't have much time for side projects these days...