Skip to content

Introducing Termpose! (To Scala)

mako yass edited this page Sep 21, 2015 · 4 revisions

Happy to announce that Termpose is finally at a stage where I can actually recommend it to people as a semi-practical thing to use! So long as you're a Scala dev! I have ported the parser to numerous other platforms, but the APIs are still a bit bare. If you're interested in helping to flesh these APIs out on any of those platforms, I would be happy to work with you.

###What actually is it Termpose is a very flexible markup language. It is decades late to the party, but if it had been a bit earlier, XML and JSON aught not to have half the primacy they presently do, and Termpose is optimistic that it will still be able to prise their withered bodies from their thrones, given time. Termpose takes the flexibility and regular semantics of S-Expressions and drops most of the parenthises. Yes, if you ever felt like S-Expressions had too many parens, to the point that they were noise, you were right, they wern't necessary, and now we don't have to put up with them.

###Begin Tutorial A simple example:

Termpose.parse("""
a
	b c
	d e
	f
	g
	hello
		ilya
		jobick
""").toString

>"Success((a (b c) (d e) f g (hello ilya jobick)))"

The Term data structure, as returned in a Try by the Termpose.parse method, is quite manageable. Each Term is either a Seqs or Stri, Seqs contains a nested sequence of terms, and Stri contain a string. However, a lot of the time you're going to wish you could just tell Termpose to type check a structure of Terms and give you the strongly typed result, and not bother leafing through primitive S-Expression deserializations. Well you can.

Let's look an example where we parse a termpose representation of a mapping from ints to sequences of booleans, for some reason.

file.terms:

11 (true)
2 (false)
3 (true false)
5
4 ()
hammersmith (eighty eighty)
import Termpose.{TypingSuccess, TypingFailure}
import Termpose.dsl._
map(int, seq(bool)).checkFile("file.terms") match {
	case TypingSuccess(misbs:Map[Int,Seq[Boolean]])=> println(misbs(11)(0)) //prints `true` :DD
	case tf:TypingFailure=> //or at least, it would have printed `true`, if it wern't for lines 4 and 6, which are of course type errors according to the typer we composed
		println(tf.toString) //prints`
		//line:4 column:0. this needs to be a (int list(bool)) pair, but it's a leaf term
		//line:6 column:0. this needs to be a int
		//line:6 column:12. this needs to be a bool
		//line:6 column:18. this needs to be a bool
}

As you can see, the typer tries to report every error it can. Rather than just hurling an inscrutable exception on arriving at the first one, which is assuredly the way you would implement your type checking, in a shameful fit of laziness and despair, if you didn't have Typer combinators and had to do it by hand on raw Terms every single time. There are also combinators that ignore errors, or transform errors into options, for instance

import Termpose.dsl._
seqAgreeable(int).check("1 2 dogs 3 4").get == Seq(1,2,3,4)
import Termpose.dsl._
seq(optional(property("a", string))).check("(a:hogs a:hags b:hors)").get ==
Seq(Some("hogs"), Some("hags"), None)

The dsl I have here is already surprising me with its flexibility, however, there's a ways to go yet. Future plans include:

  • Typers for reading case classes, via macros
  • Typers for combining results into tuples or passing them through a given lambda. Also seems like it'll require macros to do properly(effectively requires variadic type parameters).
  • Making Typers bidirectional. It seems that in most cases a Typer should be able to translate T => Term just as easily as it reads from Term => TyperResult[T]

Until I have these things, full disclosure; we are not on par with Play's JSON Format combinators and you should maybe use those instead, so long as you don't mind json's awful syntax and Play Formats' gratuitous use of implicit parameters. You probably do mind those things quite a bit, now that I have given you the hunger. So who knows what you'll do. You're crazy now.

Play's JSON combinators' emphasis on the use of implicit Typer parameters is a serious issue, I think. In a lot of cases you're going to want fine-grained control over which kind of Typer is used, for instance, you might like to use a Map typer that just ignores entries that don't fit into its requirements, rather than resolving them into errors, as the default MapTyper does. A user of the API may not realize that's possible or desirable when the composition of the Typer combinator is hidden away behind a much glorified layer of implicit magic.

Clone this wiki locally