Created
December 16, 2014 08:35
-
-
Save japgolly/fc0466d0d1690dc334af to your computer and use it in GitHub Desktop.
Quick script to decipher long Scala type errors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.language._ | |
import scala.annotation.tailrec | |
object Blah { | |
val errmsg = """ | |
[error] found : blah.webapp.client.lib.ui.Editor[(blah.webapp.base.data.Validators.blar.scala.S, (String, String, blah.webapp.base.data.ImplicationRequired)),blah.webapp.client.app.ui.CfgBlars.fields.FieldValue,scalaz.effect.IO,blah.webapp.client.app.ui.CfgBlars.storesAndState.S,blah.webapp.client.app.ui.CfgBlars.fields.Field,scalaz.effect.IO[Unit],(japgolly.scalajs.react.vdom.ReactVDom.Tag, japgolly.scalajs.react.vdom.ReactVDom.Tag, japgolly.scalajs.react.vdom.prefix_<^.Tag)] | |
[error] (which expands to) blah.webapp.client.lib.ui.Editor[((Stream[blah.webapp.base.data.CustomBlar], Option[blah.webapp.base.data.CustomBlar.Id]), (String, String, blah.webapp.base.data.ImplicationRequired)),blah.webapp.client.app.ui.CfgBlars.fields.FieldValue,scalaz.effect.IO,blah.webapp.client.app.ui.CfgBlars.storesAndState.State,blah.webapp.client.app.ui.CfgBlars.fields.Field,scalaz.effect.IO[Unit],(japgolly.scalajs.react.vdom.ReactVDom.TypedTag[japgolly.scalajs.react.ReactElement], japgolly.scalajs.react.vdom.ReactVDom.TypedTag[japgolly.scalajs.react.ReactElement], japgolly.scalajs.react.vdom.ReactVDom.TypedTag[japgolly.scalajs.react.ReactElement])] | |
[error] required: blah.webapp.client.lib.ui.Editor[?,?,scalaz.effect.IO,Bleh.this.thing.sas.S,?,scalaz.effect.IO[Unit],?] | |
[error] (which expands to) blah.webapp.client.lib.ui.Editor[?,?,scalaz.effect.IO,Bleh.this.thing.sas.State,?,scalaz.effect.IO[Unit],?] | |
""".trim | |
def main(args: Array[String]): Unit = { | |
println() | |
case class Row[V](lvl: Int, v: V) { | |
def map[A](f: V => A): Row[A] = copy[A](v = f(v)) | |
def indent = " " * lvl | |
override lazy val toString = s"$indent$v" | |
} | |
type R = List[Row[List[Char]]] | |
type R2 = List[Row[String]] | |
object Beg { | |
def unapply(c: Char): Option[Char] = if (c == '[' || c == '(') Some(c) else None | |
} | |
object End { | |
def unapply(c: Char): Option[Char] = if (c == ']' || c == ')') Some(c) else None | |
} | |
@tailrec def go(cs: List[Char], lvl: Int, cur: List[Char], r: R): R = { | |
@inline def add = if (cur.isEmpty) r else Row(lvl, cur) :: r | |
@inline def addc(c: Char) = Row(lvl, c :: cur) :: r | |
@inline def addl(c: Char) = if (cur.isEmpty && r.nonEmpty) r.head.map(c :: _) :: r.tail else addc(c) | |
cs match { | |
case Nil => add | |
case Beg(c) :: t => go(t, lvl+1, Nil , addc(c)) | |
case End(c) :: t => go(t, lvl-1, Nil , addl(c)) | |
case ',' :: t => go(t, lvl , Nil , addl(',')) | |
case h :: t => go(t, lvl , h :: cur, r) | |
} | |
} | |
def parseIntoLines(input: String): R2 = | |
go(input.toCharArray.toList, 0, Nil, Nil) | |
.foldLeft[R2](Nil)((q,r) => r.map(_.reverse.mkString.trim) :: q) | |
val errmsgs = errmsg.split("\n") | |
.filter(_ contains "expands to") | |
.map(_.replaceFirst("^.+?expands to.\\s*", "")) | |
val i = errmsgs(0) | |
val il = parseIntoLines(i) | |
val j = errmsgs(1) | |
val jl = parseIntoLines(j) | |
def sbs1(found: R2, req: R2): List[String] = { | |
val lenf = found.foldLeft(0)(_ max _.toString.length) | |
val lenr = req .foldLeft(0)(_ max _.toString.length) | |
val fmt = s"%-${lenf}s |%s| %s" | |
val qm = { | |
val p = """^\s*\?[,\]]*$""".r.pattern | |
(s: String) => p.matcher(s).matches | |
} | |
@tailrec def sbs(al: R2, bl: R2, o: List[String]): List[String] = { | |
@inline def p(a: String, b: String) = | |
String.format(fmt, a, | |
if (a.isEmpty || b.isEmpty || a==b) " " else if (qm(a) || qm(b)) "?" else "≠", | |
b) :: o | |
@inline implicit def auto(a: Row[String]): String = a.toString | |
(al,bl) match { | |
case (Nil , Nil ) => o | |
case (a :: x, Nil ) => sbs(x, Nil, p(a, "")) | |
case (Nil , b :: y) => sbs(Nil, y, p("", b)) | |
case (a :: x, b :: y) => | |
if (a.lvl == b.lvl) | |
sbs(x, y, p(a,b)) | |
else if (a.lvl > b.lvl) | |
sbs(x, bl, p(a, "")) | |
else | |
sbs(al, y, p("", b)) | |
} | |
} | |
val hd = String.format(fmt, "Found", " ", "Required") | |
val hr = s"${"-"*lenf}-+-+-${"-"*lenr}" | |
val rr = hd :: hr :: sbs(found, req, Nil).reverse | |
hr :: rr ::: hr :: Nil | |
} | |
sbs1(jl, il) foreach println | |
println() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment