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

shapeless.ops.unions.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-15 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
package ops

import labelled.field
import poly._

object union {
  import shapeless.labelled.FieldType

  /**
   * Type class supporting union member selection.
   *
   * @author Miles Sabin
   */
  @annotation.implicitNotFound(msg = "No field ${K} in union ${C}")
  trait Selector[C <: Coproduct, K] extends Serializable {
    type V
    type Out = Option[V]
    def apply(l : C): Out
  }

  trait LowPrioritySelector {
    type Aux[C <: Coproduct, K, V0] = Selector[C, K] { type V = V0 }

    implicit def tlSelector[H, T <: Coproduct, K]
      (implicit st : Selector[T, K]): Aux[H :+: T, K, st.V] =
        new Selector[H :+: T, K] {
          type V = st.V
          def apply(u : H :+: T): Out = u match {
            case Inl(l) => None
            case Inr(r) => if(st == null) None else st(r)
          }
        }
  }

  object Selector extends LowPrioritySelector {
    def apply[C <: Coproduct, K](implicit selector: Selector[C, K]): Aux[C, K, selector.V] = selector

    implicit def hdSelector[K, V0, T <: Coproduct]: Aux[FieldType[K, V0] :+: T, K, V0] =
      new Selector[FieldType[K, V0] :+: T, K] {
        type V = V0
        def apply(u : FieldType[K, V] :+: T): Out = u match {
          case Inl(l) => Some(l)
          case Inr(r) => None
        }
      }
  }

  /**
   * Type class supporting collecting the keys of a union as an `HList`.
   * 
   * @author Miles Sabin
   */
  trait Keys[U <: Coproduct] extends DepFn0 with Serializable { type Out <: HList }

  object Keys {
    def apply[U <: Coproduct](implicit keys: Keys[U]): Aux[U, keys.Out] = keys

    type Aux[U <: Coproduct, Out0 <: HList] = Keys[U] { type Out = Out0 }

    implicit def cnilKeys[U <: CNil]: Aux[U, HNil] =
      new Keys[U] {
        type Out = HNil
        def apply(): Out = HNil
      }

    implicit def coproductKeys[K, V, T <: Coproduct](implicit wk: Witness.Aux[K], kt: Keys[T]): Aux[FieldType[K, V] :+: T, K :: kt.Out] =
      new Keys[FieldType[K, V] :+: T] {
        type Out = K :: kt.Out
        def apply(): Out = wk.value :: kt()
      }
  }

  /**
   * Type class supporting collecting the value of a union as a `Coproduct`.
   * 
   * @author Miles Sabin
   */
  trait Values[U <: Coproduct] extends DepFn1[U] with Serializable { type Out <: Coproduct }

  object Values {
    def apply[U <: Coproduct](implicit values: Values[U]): Aux[U, values.Out] = values

    type Aux[U <: Coproduct, Out0 <: Coproduct] = Values[U] { type Out = Out0 }

    implicit def cnilValues[U <: CNil]: Aux[U, CNil] =
      new Values[U] {
        type Out = CNil
        def apply(u: U): Out = u
      }

    implicit def coproductValues[K, V, T <: Coproduct](implicit vt: Values[T]): Aux[FieldType[K, V] :+: T, V :+: vt.Out] =
      new Values[FieldType[K, V] :+: T] {
        type Out = V :+: vt.Out
        def apply(l: FieldType[K, V] :+: T): Out = l match {
          case Inl(l) => Inl(l)
          case Inr(r) => Inr(vt(r))
        }
      }
  }

  /**
   * Type class supporting converting this union to a `Coproduct` of key-value pairs.
   *
   * @author Alexandre Archambault
   */
  trait Fields[U <: Coproduct] extends DepFn1[U] with Serializable {
    type Out <: Coproduct
  }

  object Fields {
    def apply[U <: Coproduct](implicit fields: Fields[U]): Aux[U, fields.Out] = fields

    type Aux[L <: Coproduct, Out0 <: Coproduct] = Fields[L] { type Out = Out0 }

    implicit val cnilFields: Aux[CNil, CNil] =
      new Fields[CNil] {
        type Out = CNil
        def apply(u: CNil) = u
      }

    implicit def cconsFields[K, V, T <: Coproduct](implicit
      key: Witness.Aux[K],
      tailFields: Fields[T]
    ): Aux[FieldType[K, V] :+: T, (K, V) :+: tailFields.Out] =
      new Fields[FieldType[K, V] :+: T] {
        type Out = (K, V) :+: tailFields.Out
        def apply(u: FieldType[K, V] :+: T) =
          u match {
            case Inl(v) => Inl(key.value -> v)
            case Inr(t) => Inr(tailFields(t))
          }
      }
  }

  /**
   * Type class combining `Keys` and `Values` for convenience and compilation speed.
   * It's similar to `Fields`, but produces distinct `HList` and `Coproduct`
   * instead of a zipped `Coproduct`.
   *
   * @author Jisoo Park
   */
  trait UnzipFields[L <: Coproduct] extends Serializable {
    type Keys <: HList
    type Values <: Coproduct

    def keys(): Keys
    def values(u: L): Values
  }

  object UnzipFields {
    def apply[L <: Coproduct](implicit uf: UnzipFields[L]): Aux[L, uf.Keys, uf.Values] = uf

    type Aux[L <: Coproduct, K <: HList, V <: Coproduct] = UnzipFields[L] { type Keys = K; type Values = V }

    implicit def cnilUnzipFields[L <: CNil]: Aux[L, HNil, L] =
      new UnzipFields[L] {
        type Keys = HNil
        type Values = L
        def keys() = HNil
        def values(u: L): L = u
      }

    implicit def cconsUnzipFields[K, V, T <: Coproduct](implicit
      key: Witness.Aux[K],
      tailUF: UnzipFields[T]
    ): Aux[FieldType[K, V] :+: T, K :: tailUF.Keys, V :+: tailUF.Values] =
      new UnzipFields[FieldType[K, V] :+: T] {
        type Keys = K :: tailUF.Keys
        type Values = V :+: tailUF.Values

        def keys() = key.value :: tailUF.keys()
        def values(u: FieldType[K, V] :+: T) =
          u match {
            case Inl(v) => Inl(v)
            case Inr(t) => Inr(tailUF.values(t))
          }
      }
  }

  /**
   * Type class supporting converting this union to a `Map` whose keys and values
   * are typed as the Lub of the keys and values of this union.
   *
   * @author Alexandre Archambault
   */
  trait ToMap[U <: Coproduct] extends DepFn1[U] with Serializable {
    type Key
    type Value
    type Out = Map[Key, Value]
  }

  object ToMap {
    def apply[U <: Coproduct](implicit toMap: ToMap[U]): Aux[U, toMap.Key, toMap.Value] = toMap

    type Aux[U <: Coproduct, Key0, Value0] = ToMap[U] { type Key = Key0; type Value = Value0 }

    implicit def cnilToMap[K, V]: Aux[CNil, K, V] =
      new ToMap[CNil] {
        type Key = K
        type Value = V
        def apply(l: CNil) = Map.empty
      }

    implicit val cnilToMapAnyNothing: Aux[CNil, Any, Nothing] = cnilToMap[Any, Nothing]

    implicit def csingleToMap[K, V](implicit
      wk: Witness.Aux[K]
    ): Aux[FieldType[K, V] :+: CNil, K, V] =
      new ToMap[FieldType[K, V] :+: CNil] {
        type Key = K
        type Value = V
        def apply(c: FieldType[K, V] :+: CNil) = (c: @unchecked) match {
          case Inl(h) => Map(wk.value -> (h: V))
        }
      }

    implicit def coproductToMap[HK, HV, TH, TT <: Coproduct, TK, TV, K, V](implicit
      tailToMap: ToMap.Aux[TH :+: TT, TK, TV],
      keyLub: Lub[HK, TK, K],
      valueLub: Lub[HV, TV, V],
      wk: Witness.Aux[HK]
    ): Aux[FieldType[HK, HV] :+: TH :+: TT, K, V] =
      new ToMap[FieldType[HK, HV] :+: TH :+: TT] {
        type Key = K
        type Value = V
        def apply(c: FieldType[HK, HV] :+: TH :+: TT) = c match {
          case Inl(h) => Map(keyLub.left(wk.value) -> valueLub.left(h: HV))
          case Inr(t) => tailToMap(t).map{case (k, v) => keyLub.right(k) -> valueLub.right(v)}
        }
      }
  }
  
  /**
   * Type class supporting mapping a higher rank function over the values of a union.
   *
   * @author Alexandre Archambault
   */
  trait MapValues[HF, U <: Coproduct] extends DepFn1[U] with Serializable { type Out <: Coproduct }

  object MapValues {
    def apply[HF, U <: Coproduct](implicit mapValues: MapValues[HF, U]): Aux[HF, U, mapValues.Out] = mapValues

    type Aux[HF, U <: Coproduct, Out0 <: Coproduct] = MapValues[HF, U] { type Out = Out0 }

    implicit def cnilMapValues[HF]: Aux[HF, CNil, CNil] =
      new MapValues[HF, CNil] {
        type Out = CNil
        def apply(c: CNil) = c
      }

    implicit def cconsMapValues[HF, K, V, T <: Coproduct](implicit
      hc: Case1[HF, V],
      tailMapValues: MapValues[HF, T]
    ): Aux[HF, FieldType[K, V] :+: T, FieldType[K, hc.Result] :+: tailMapValues.Out] =
      new MapValues[HF, FieldType[K, V] :+: T] {
        type Out = FieldType[K, hc.Result] :+: tailMapValues.Out
        def apply(c: FieldType[K, V] :+: T) = c match {
          case Inl(h) => Inl(field[K](hc(h: V)))
          case Inr(t) => Inr(tailMapValues(t))
        }
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy