Scala Pattern Matching pretty printed

不羁岁月 提交于 2019-12-07 03:20:57

问题


Is that possible to somehow marshall PartialFunction (let's assume it will always contains only one case) into something human-readable?

Let's say we have collection of type Any (messages: List[Any]) and number of PartialFuntion[Any, T] defined using pattern matching block.

case object R1
case object R2
case object R3

val pm1: PartialFunction[Any, Any] = {
  case "foo" => R1
}

val pm2: PartialFunction[Any, Any] = {
  case x: Int if x > 10 => R2
}

val pm3: PartialFunction[Any, Any] = {
  case x: Boolean => R3
}

val messages: List[Any] = List("foo", 20)
val functions = List(pm1, pm2)

then we can find all the messages matched by provided PFs and related applications

val found: List[Option[Any]] = functions map { f =>
  messages.find(f.isDefined).map(f)
}

but what if I need resulting map of 'what I expect' to 'what I've got' in the human-readable form (for logging). Say,

(case "foo")         -> Some(R1)
(case Int if _ > 10) -> Some(R2)
(case Boolean)       -> None

Is that possible? Some macro/meta works?


回答1:


There's nothing at runtime which will print compiled code nicely.

You could write a macro which will print the source code of the tree and use that? Most macro tutorials start with a macro for printing source code -- see e.g. http://www.warski.org/blog/2012/12/starting-with-scala-macros-a-short-tutorial/

Perhaps:

// Given a partial function "pf", return the source code for pf
// as a string as well as the compiled, runnable function itself
def functionAndSource(pf: PartialFunction[Any, Any]): (String, PartialFunction[Any, Any]) = macro functionAndSourceImpl

def functionAndSourceImpl ...

val pm1: (String, PartialFunction[Any, Any]) = functionAndSource {
  case "foo" => R1
}

This isn't ever going to be that easy or nice in Scala. Scala isn't Lisp or Ruby: it's a compiled language and it is not optimised for reflection on the code itself.




回答2:


Thanks for your answers. Using Macro is interesting one choice. But as an option the solution might be to use kind of named partial functions. The idea is to name the function so in the output you can see the name of function instead source code.

object PartialFunctions {

  type FN[Result] = PartialFunction[Any, Result]

  case class NamedPartialFunction[A,B](name: String)(pf: PartialFunction[A, B]) extends PartialFunction[A,B] {
    override def isDefinedAt(x: A): Boolean = pf.isDefinedAt(x)
    override def apply(x: A): B = pf.apply(x)
    override def toString(): String = s"matching($name)"
  }

  implicit class Named(val name: String) extends AnyVal {
    def %[A,B](pf: PartialFunction[A,B]) = new NamedPartialFunction[A, B](name)(pf)
  }

}

So then you can use it as follows

import PartialFunctions._

val pm1: PartialFunction[Any, Any] = "\"foo\"" % {
  case "foo" => R1
}

val pm2: PartialFunction[Any, Any] = "_: Int > 10" % {
  case x: Int if x > 10 => R2
}

val pm3: PartialFunction[Any, Any] = "_: Boolean" % {
  case x: Boolean => R3
}

val messages: List[Any] = List("foo", 20)
val functions = List(pm1, pm2)

val found: List[Option[(String, Any)]] = functions map { case f: NamedPartialFunction =>
  messages.find(f.isDefined).map(m => (f.name, f(m))
}


来源:https://stackoverflow.com/questions/31037347/scala-pattern-matching-pretty-printed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!