Skip to content
jsalvata edited this page Nov 6, 2011 · 2 revisions

This code was developped as a proof-of-concept before development of Typed XML even began. So it doesn't really use any XML, even less the current unmarshaller API -- at that time I was thinking of using operator symbols for most of the API methods -- but it shows the idea.

It is possible that uncurrying would have given a better solution than HLists, but it would be limited in the number of parameters it can take. Or maybe we should just leave the function fully curried and let the programmer add the parenthesis.

package com.gremideprogramadors.nmlb.xml

package exploration

import collection.mutable.LinkedList
import exploration.I18nUnmarshaller.MsgTag

/**
 * Heterogeneous lists idea from
 * http://jnordenberg.blogspot.com/2008/08/hlist-in-scala.html
 * with some changes -- so bugs are mine.
 */
sealed trait HTList

final case class HTCons[L <: HTList, T](list : L, last : T) extends HTList {
  def &[T](v : T) = HTCons(this, v)
}

final class HTNil extends HTList {
  def &[T](v : T) = HTCons(this, v)
}

// aliases for building HList types and for pattern matching
object HTList {
  type &[L <: HTList, T] = HTCons[L, T]
  val & = HTCons
  val htNil = new HTNil
  implicit def any2HTList[T](v: T) = htNil & v
    // Q: is this dangerous? Count of bugs caused by it: 0 since 8-oct-2011
}

import HTList._

/**
 * Example: slightly more advanced internationalization.
 *
 * Extend the previous example with placeholders (in this case, for numeric arguments).
 */
object I18nTest extends App {
  println(<msg name="chance">Pay each player <currency/> and <currency/> to the bank.</msg>)
  val msg= I18nUnmarshaller.msg.name("chance").++("Pay each player ").currency./.++(" and ").currency./.++(" to the bank.")./=
  println(msg(2.2 & 3.3))
  // Note that msg(2.2 & "hello") throws a type error when compìling
}

abstract class Msg[P <: HTList](name: Option[String], text: String) {
  def apply(i: P): String
}

object I18nUnmarshaller {
  def msg= new MsgTag[HTNil] {
    def /= = new Msg[HTNil](_name, s.toString) {
      def apply(p: HTNil) = s.toString
    }
  }

  abstract class MsgTag[P <: HTList] {
  this_tag =>
    protected var _name: Option[String]= None
    def name(s: String) = { _name= Some(s); this }

    protected val s= new StringBuilder()
    def ++(s: String) = { this.s++= s; this }

    def currency = new MsgTag[P & Double] {
      def / = this

      def /= = new Msg[P & Double](_name, s.toString) {
        def apply(p: P & Double) = p match {
          case i & n => this_tag./=(i) + n.toString + "€" + s
        }
      }
    }

    def /= : Msg[P]
  }
}
Clone this wiki locally