Implicitly converting map to function

Here’s problem I’ve encountered several times. If I have a function which has a parameter of the form A=>B, but I have a Map[A,B], why can’t I pass that instead? Likewise if I have an object X with an apply method of type A=>B. In the case of an object, I can pass (X _). But If I try the same thing with a map, I get an all too expected error.

Error:(52, 60) _ must follow method; cannot follow scala.collection.immutable.Map[lecture.graph.Edge[Int],Int]
      val Some(Path(vertices,edges)) = g.shortestPath(1,6,(weight _))

Turns out I can use (weight.apply _) and everyone is happy. Is that what I’m supposed to be doing? Seems obscure. On the other hand, it’s not much typing.

    {
      val weight = Map(Edge(1,3)->1,
                         Edge(2,3)->1,
                         Edge(3,4)->1,
                         Edge(4,6)->5,
                         Edge(4,5)->1,
                         Edge(5,6)->1)

      val Some(Path(vertices,edges)) = g.shortestPath(1,6,(weight.apply _))
      println(s"vertices=$vertices edges=$edges")
    }
1 Like

Something worth noting here: Map is actually a PartialFunction, so if your target function was expecting A => Option[B], you could simply use weight.lift and it would work.

But in this case, I think your weight.apply is about the best you can do, because you’re being a bit unsafe. (At least from the type perspective.) That’s because the Map might not contain the target key, and thus it might throw an Exception.

That’s why there’s no built-in for this and you have to spell it out. You may have outside knowledge that the Map is complete, but the compiler has no way of knowing that…

1 Like

That’s right. The fact that you can’t directly use a Map[A, B] as a function A => B is an implementation detail, a consequence of how the Map trait is declared:

trait Map[K, +V] extends Iterable[(K, V)] with GenMap[K, V] with MapLike[K, V, Map[K, V]] 

I.e. it doesn’t inherit from A => B therefore can’t be upcast to it.

1 Like

Confusion. Maps are functions.

@ val a: Int => String = Map(1 -> "x") 
a: Int => String = Map(1 -> "x")

@ Set(1, 2, 3).map(Map(1 -> "x", 2 -> "y", 3 -> "z")) 
res6: Set[String] = Set("x", "y", "z")

@ implicitly[Map[Int, String] <:< (Int => String)] 
res7: Map[Int, String] <:< Int => String = <function1>
2 Likes

Yup, you’re right. I didn’t look hard enough! Map[K, V] indeed has supertype K => V.

In fact I just went and double-checked @jimka’s issue, but it seems to be working for me:

scala> def f(x: Int, g: Int => String): String = g(x)
f: (x: Int, g: Int => String)String

scala> f(1, Map(1 -> "a"))
res1: String = a

Perhaps you’re running an older Scala version, @jimka? Although I don’t see it making much of a difference outside of bugfixes.

Remarkable. It works for me as well. Thanks for the help. But it sounds like this feature is also a surprise to some experts as well as beginners.

… huh. For some reason, that’s not showing very obviously in the Scaladocs. PartialFunction comes in through MapLike, which is what I had found. Function1 seems to be getting pulled in via GenMapLike, but the Scaladoc for the latter doesn’t actually say that. Odd – I’m clearly missing a subtlety here…

Look at the ‘Linear Supertypes’ section, it’s listed there among a long list of other supertypes.

What’s a “linear supertype” BTW? Are there any other kind of supertypes?

I think it just means ‘a linear list view of the supertype graph’.

Oh man, now I’m thinking. Are there probabilistic type systems?

val a: Long0.99 = 3L
val b: Long0.99 = 4L
val c: Long1.00 = a * b

would compile 98.01% of the time.

Seems like this could have an application to NLP.