Skip to content

Introducing Termpose! (To Scala)

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

Happy to announce that Termpose is at a stage where I can recommend it to people as a fairly joyous thing to use, so long as you're a Scala dev. I have ported the parser to like 5 other languages, but those APIs only provide you with String => Term functionality. In this article, I'll be explaining the Iterator[Char]|File|String|Term => T functionality, that takes a term(or a term source) checks that it conforms to the specified representation of a certain type, then gives you a result of that type. ( If you're interested in helping to flesh these APIs out onto any of those other platforms, let's chat https://gitter.im/makoConstruct/termpose )

###What actually is Termpose Termpose is an extremely flexible markup language. Termpose takes the flexibility and regular semantics of S-Expressions and drops most of the parentheses, which also makes it an extremely pretty, minimal markup language. If you ever felt like S-Expressions had too many parens, to the point that they were noise, it turns out you were right to. Where proper indentation is present, parens are frequently redundant.

###Begin Tutorial A simple example:

Termpose.parse("""

production_spire_A
	drone
		name mary shmidt
		projected_expiry 2032
		caste accounting
	drone
		name harry jordan
		projected_expiry 2067
		caste sales
	drone name(clarice wilkins) projected_expiry:2080 caste:advertising
	(drone  (name "jarae" "hillfolk")  (projected_expiry "2075")  (caste "web development"))

""").toString

>Success((production_spire_A (drone (name mary shmidt) (projected_expiry 2032) (caste accounting)) (drone (name harry jordan) (projected_expiry 2067) (caste sales)) (drone (name clarice wilkins) (projected_expiry 2080) (caste marketing)) (drone (name jarae hillfolk) (projected_expiry 2075) (caste "web development"))))

The Termpose.parse method returns a Try[Term]. The term data structure is quite manageable. Each Term is either a Seqs or Stri, Seqs contains a nested sequence of terms, and Stri contain just 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 a strongly typed data structure of the stated type, so that you don't have to bother leafing through primitive S-Expression deserializations yourself and check for super banal requirements by hand.

Well ya can. Let's try an example where we type termpose into a Map[Int, Seq[Boolean]] 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 a programmer would implement their type checking, in a shameful fit of laziness and despair, if they didn't have Typer combinators.

There are also combinators that ignore errors, or transform trees with 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:

  • Macros for generating Typers for user defined classes
  • 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]

Till then, Sincerely, Mako

Clone this wiki locally