All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shapeless.labelled.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-16 Miles Sabin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package shapeless

import scala.language.experimental.macros

import scala.reflect.macros.whitebox

object labelled {
  /**
   * The type of fields with keys of singleton type `K` and value type `V`.
   */
  type FieldType[K, +V] = V with KeyTag[K, V]
  trait KeyTag[K, +V]

  /**
   * Yields a result encoding the supplied value with the singleton type `K' of its key.
   */
  def field[K] = new FieldBuilder[K]

  class FieldBuilder[K] {
    def apply[V](v : V): FieldType[K, V] = v.asInstanceOf[FieldType[K, V]]
  }
}

trait DefaultSymbolicLabelling[T] extends DepFn0 with Serializable { type Out <: HList }

object DefaultSymbolicLabelling {
  type Aux[T, Out0] = DefaultSymbolicLabelling[T] { type Out = Out0 }

  def apply[T](implicit lab: DefaultSymbolicLabelling[T]): Aux[T, lab.Out] = lab

  implicit def mkDefaultSymbolicLabelling[T]: DefaultSymbolicLabelling[T] =
    macro LabelledMacros.mkDefaultSymbolicLabellingImpl[T]
}

/**
 * Polymorphic function that allows modifications on record fields while preserving the
 * original key types.
 *
 * @author Dario Rexin
 */
trait FieldPoly extends Poly1 {
  import labelled._

  class FieldCaseBuilder[A, T] {
    def apply[Res](fn: A => Res) = new Case[FieldType[T, A]] {
      type Result = FieldType[T, Res]
      val value: Function1[A :: HNil, FieldType[T, Res]] =
        (l: A :: HNil) => field[T](fn(l.head))
    }
  }

  def atField[A](w: Witness) = new FieldCaseBuilder[A, w.T]
}

/**
 * Field with values of type `V`.
 *
 * Record keys of this form should be objects which extend this trait. Keys may also be arbitrary singleton typed
 * values, however keys of this form enforce the type of their values.
 *
 * @author Miles Sabin
 */
trait FieldOf[V] {
  import labelled._

  type F = FieldType[this.type, V]

  def ->>(v: V): FieldType[this.type, V] = field[this.type](v)
}

@macrocompat.bundle
class LabelledMacros(val c: whitebox.Context) extends SingletonTypeUtils with CaseClassMacros {
  import labelled._
  import c.universe._

  def mkDefaultSymbolicLabellingImpl[T](implicit tTag: WeakTypeTag[T]): Tree = {
    val tTpe = weakTypeOf[T]
    val labels: List[String] =
      if(isProduct(tTpe)) fieldsOf(tTpe).map { f => nameAsString(f._1) }
      else if(isCoproduct(tTpe)) ctorsOf(tTpe).map { tpe => nameAsString(nameOf(tpe)) }
      else c.abort(c.enclosingPosition, s"$tTpe is not case class like or the root of a sealed family of types")

    val labelTpes = labels.map(SingletonSymbolType(_))
    val labelValues = labels.map(mkSingletonSymbol)

    val labelsTpe = mkHListTpe(labelTpes)
    val labelsValue = mkHListValue(labelValues)

    q"""
      new _root_.shapeless.DefaultSymbolicLabelling[$tTpe] {
        type Out = $labelsTpe
        def apply(): $labelsTpe = $labelsValue
      } : _root_.shapeless.DefaultSymbolicLabelling.Aux[$tTpe, $labelsTpe]
    """
  }

  def recordTypeImpl(tpeSelector: Tree): Tree =
    labelledTypeImpl(tpeSelector, "record", hnilTpe, hconsTpe)

  def unionTypeImpl(tpeSelector: Tree): Tree =
    labelledTypeImpl(tpeSelector, "union", cnilTpe, cconsTpe)

  def labelledTypeImpl(tpeSelector: Tree, variety: String, nilTpe: Type, consTpe: Type): Tree = {
    def mkFieldTpe(keyTpe: Type, valueTpe: Type): Type =
      appliedType(fieldTypeTpe, List(keyTpe, valueTpe))

    val q"${tpeString: String}" = tpeSelector
    val fields =
      if (tpeString.trim.isEmpty)
        Array.empty[(Type, Type)]
      else
        tpeString.split(",").map(_.trim).map(_.split("->").map(_.trim)).map {
          case Array(key, value) =>
            val keyTpe =
              parseLiteralType(key)
                .getOrElse(c.abort(c.enclosingPosition, s"Malformed literal type $key"))

            val valueTpe =
              parseType(value)
                .getOrElse(c.abort(c.enclosingPosition, s"Malformed literal or standard type $value"))

            (keyTpe, valueTpe)

          case other =>
            c.abort(c.enclosingPosition, s"Malformed $variety type $tpeString")
        }

    val labelledTpe =
      fields.foldRight(nilTpe) { case ((keyTpe, valueTpe), acc) =>
        val fieldTpe = mkFieldTpe(keyTpe, valueTpe)
        appliedType(consTpe, List(fieldTpe, acc))
      }

    typeCarrier(labelledTpe)
  }

  def hlistTypeImpl(tpeSelector: Tree): Tree =
    nonLabelledTypeImpl(tpeSelector, "hlist", hnilTpe, hconsTpe)

  def coproductTypeImpl(tpeSelector: Tree): Tree =
    nonLabelledTypeImpl(tpeSelector, "coproduct", cnilTpe, cconsTpe)

  def nonLabelledTypeImpl(tpeSelector: Tree, variety: String, nilTpe: Type, consTpe: Type): Tree = {
    val q"${tpeString: String}" = tpeSelector
    val elemTypes =
      if (tpeString.trim.isEmpty)
        Array.empty[Type]
      else
        tpeString.split(",").map(_.trim).map { elemTypeStr =>
          parseType(elemTypeStr)
            .getOrElse(c.abort(c.enclosingPosition, s"Malformed literal or standard type $elemTypeStr"))
        }

    val tpe =
      elemTypes.foldRight(nilTpe) { case (elemTpe, acc) =>
        appliedType(consTpe, List(elemTpe, acc))
      }

    typeCarrier(tpe)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy