jvliwanag
jvliwanag

Reputation: 1598

Typesafe keys for a map

Given the following code:

val m: Map[String, Int] = .. // fetch from somewhere
val keys: List[String] = m.keys.toList
val keysSubset: List[String] = ... // choose random keys

We can define the following method:

def sumValues(m: Map[String, Int], ks: List[String]): Int =
  ks.map(m).sum

And call this as:

sumValues(m, keysSubset)

However, the problem with sumValues is that if ks happens to have a key not present on the map, the code will still compile but throw an exception at runtime. Ex:

// assume m = Map("two" -> 2, "three" -> 3)
sumValues(m, 1 :: Nil)

What I want instead is a definition for sumValues such that the ks argument should, at compile time, be guaranteed to only contain keys that are present on the map. As such, my guess is that the existing sumValues type signature needs to accept some form of implicit evidence that the ks argument is somehow derived from the list of keys of the map.

I'm not limited to a scala Map however, as any record-like structure would do. The map structure however won't have a hardcoded value, but something derived/passed on as an argument.

Note: I'm not really after summing the values, but more of figuring out a type signature for sumValues whose calls to it can only compile if the ks argument is provably from the list of keys the map (or record-like structure).

Upvotes: 1

Views: 322

Answers (1)

Dnomyar
Dnomyar

Reputation: 727

Another solution could be to map only the intersection (i.e. : between m keys and ks).

For example :

scala> def sumValues(m: Map[String, Int], ks: List[String]): Int = {
 |   m.keys.filter(ks.contains).map(m).sum
 | }
sumValues: (m: Map[String,Int], ks: List[String])Int

scala> val map = Map("hello" -> 5)
map: scala.collection.immutable.Map[String,Int] = Map(hello -> 5)

scala> sumValues(map, List("hello", "world"))
res1: Int = 5

I think this solution is better than providing a default value because more generic (i.e. : you can use it not only with sums). However, I guess that this solution is less effective in term of performance because the intersection.

EDIT : As @jwvh pointed out in it message below, ks.intersect(m.keys.toSeq).map(m).sum is, to my opinion, more readable than m.keys.filter(ks.contains).map(m).sum.

Upvotes: 1

Related Questions