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

shapeless.coproduct.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-14 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.dynamics
import scala.language.experimental.macros

import scala.annotation.tailrec

/** Encodes a coproduct type, such as a sealed family of case classes.
  *
  * Each constructor from the family gets an encoding in terms of nested Inr and Inl.
  *
  * Which constructor is encoded as Inl() and which as Inr(Inl()) is determined by lexical order
  * of the subclasses. This example illustrates the encoding:
  *
  * {{{
  * scala> sealed trait Animal
  * defined trait Animal
  *
  * scala> case class Cat(name: String, livesLeft: Int) extends Animal
  * defined class Cat
  *
  * scala> case class Dog(name: String, bonesBuried: Int) extends Animal
  * defined class Dog
  *
  * scala> case class Koala(name: String, leavesEaten: Int) extends Animal
  * defined class Koala
  *
  * scala> case class Sloth(name: String, daysToClimbDownFromCurrentTree: Int) extends Animal
  * defined class Sloth
  *
  * scala> val garfield = Cat("Garfield", 9)
  * garfield: Cat = Cat(Garfield,9)
  *
  * scala> val odie = Dog("Odie", 3)
  * odie: Dog = Dog(Odie,3)
  *
  * scala> val koala = Koala("foo", 10)
  * koala: Koala = Koala(foo,10)
  *
  * scala> val sloth = Sloth("bar", 2)
  * sloth: Sloth = Sloth(bar,2)
  *
  * scala> val genAnimal = Generic[Animal]
  * genAnimal: shapeless.Generic[Animal]{type Repr = Cat :+: Dog :+: Koala :+: Sloth} = ...
  *
  * scala> def showCoproduct(o: Any) : String = o match {
  *      | case Inl(a) => "Inl(" + showCoproduct(a) + ")"
  *      | case Inr(a) => "Inr(" + showCoproduct(a) + ")"
  *      | case a => a.toString
  *      | }
  * showCoproduct: (o: Any)String
  *
  * scala> showCoproduct(genAnimal.to(garfield))
  * res5: String = Inl(Cat(Garfield,9))
  *
  * scala> showCoproduct(genAnimal.to(odie))
  * res6: String = Inr(Inl(Dog(Odie,3)))
  *
  * scala> showCoproduct(genAnimal.to(koala))
  * res7: String = Inr(Inr(Inl(Koala(foo,10))))
  *
  * scala> showCoproduct(genAnimal.to(sloth))
  * res8: String = Inr(Inr(Inr(Inl(Sloth(bar,2)))))
  *
  * scala>
  * }}}
  */
sealed trait Coproduct extends Product with Serializable

/** Like Either, the :+: type defines a new type that can contain either H or T.
  */
sealed trait :+:[+H, +T <: Coproduct] extends Coproduct {
  /**
   * Non-recursive fold (like Either#fold)
   */
  def eliminate[A](l: H => A, r: T => A): A
}

/** `H :+: T` can either be `H` or `T`.
  * In this case it is `H`.
  */
final case class Inl[+H, +T <: Coproduct](head : H) extends :+:[H, T] {
  override def eliminate[A](l: H => A, r: T => A) = l(head)
}

/** `H :+: T` can either be `H` or `T`.
  * In this case it is `T`.
  */
final case class Inr[+H, +T <: Coproduct](tail : T) extends :+:[H, T] {
  override def eliminate[A](l: H => A, r: T => A) = r(tail)
}

/** The CNil type is used to terminate a 'list' of :+: alternatives.
  *
  * Like the Nil constructor of List, it does not convey real information.
  * This is achieved by not having any value for CNil.
  *
  * This makes the type `Int :+: CNil` equivalent to `Int`, because the right (`Inr`) alternative
  * of `:+:` can not be constructed properly.
  */
sealed trait CNil extends Coproduct {
  /** Call this when you hit the CNil case in pattern matching to make the match exhaustive and safe. */
  def impossible: Nothing
}

object Coproduct extends Dynamic {
  import ops.coproduct.Inject
  import ops.coproduct.RuntimeInject
  import syntax.CoproductOps

  class MkCoproduct[C <: Coproduct] {
    def apply[T](t: T)(implicit inj: Inject[C, T]): C = inj(t) 
  }
  
  def apply[C <: Coproduct] = new MkCoproduct[C]

  implicit def cpOps[C <: Coproduct](c: C) = new CoproductOps(c) 

  def unsafeMkCoproduct(length: Int, value: Any) =
    (0 until length).foldLeft[Coproduct](Inl(value))((accum, _) => Inr(accum))

  @tailrec
  def unsafeGet(c: Coproduct): Any = c match {
    case Inl(h) => h
    case Inr(c) => unsafeGet(c)
  }


  /**
   * Allows to specify a `Coproduct` type with a syntax similar to `Record` and `Union`, as follows,
   *
   * {{{
   * type ISB = Coproduct.`Int, String, Boolean`.T
   * }}}
   *
   * Literal types are allowed, so that the following is valid,
   *
   * {{{
   * type ABC = Coproduct.`'a, 'b, 'c`.T
   * type TwoTrueStr = Coproduct.`2, true, "str"`.T
   * }}}
   */
  def selectDynamic(tpeSelector: String): Any = macro LabelledMacros.coproductTypeImpl

  /** Allows to inject a runtime value of type `Any` in a `Coproduct` */
  def runtimeInject[C <: Coproduct](x: Any)(implicit rinj: RuntimeInject[C]): Option[C] = rinj(x)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy