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

shapeless.sized.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011-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.collection.{ BuildFrom, Factory, Iterable, IterableOps }
import scala.collection.generic.IsIterableLike

/**
 * Wrapper for a collection type witnessing that it has the statically specified length. Can be
 * applied to any type which can be viewed as an `IterableOps`, ie. standard collections,
 * `Array`s, `String`s etc.
 * 
 * @author Miles Sabin
 */
final class Sized[+Repr, L <: Nat] private (val unsized : Repr) {
  // Cannot extend AnyVal in 2.10, see https://issues.scala-lang.org/browse/SI-6260
  override def toString = unsized.toString

  override def equals(other: Any): Boolean =
    other match {
      case o: Sized[_, _] => unsized.equals(o.unsized)
      case _ => false
    }

  override def hashCode: Int = unsized.hashCode
}

/**
 * Carrier for `Sized` operations.
 * 
 * These operations are implemented here as extension methods of the minimal `Sized` type to avoid issues that would
 * otherwise be caused by its covariance.
 * 
 * @author Miles Sabin
 */
class SizedOps[A0, Repr : AdditiveCollection, L <: Nat](s : Sized[Repr, L], itl: IsIterableLike[Repr] { type A = A0 }) { outer =>
  import nat._
  import ops.nat._
  import LT._
  import Sized.wrap
  import ops.sized._
  import ops.hlist.Tupler

  private implicit def conv(repr: Repr): IterableOps[A0, Iterable, Repr] = itl.conversion(repr)

  /**
   * Returns the ''nth'' element of this `Sized`. Available only if there is evidence that this `Sized` has at least ''n''
   * elements.
   */
  def apply[N <: Nat](implicit diff: Diff[L, Succ[N]], ev: ToInt[N]): A0 = s.unsized.drop(toInt[N]).head

  /**
   * Returns the ''nth'' element of this `Sized`. Available only if there is evidence that this `Sized` has at least ''n''
   * elements.
   */
  def apply(n: Nat)(implicit diff: Diff[L, Succ[n.N]], ev: ToInt[n.N]): A0 = apply[n.N]
  
  /**
   * Returns the ''nth'' element of this `Sized`. Available only if there is evidence that this `Sized` has at least ''n''
   * elements.
   */
  def at[N <: Nat](implicit diff: Diff[L, Succ[N]], ev: ToInt[N]): A0 = apply[N]

  /**
   * Returns the ''nth'' element of this `Sized`. Available only if there is evidence that this `Sized` has at least ''n''
   * elements.
   */
  def at(n: Nat)(implicit diff: Diff[L, Succ[n.N]], ev: ToInt[n.N]): A0 = apply[n.N]

  /**
   * Returns the head of this collection. Available only if there is evidence that this collection has at least one
   * element.
   */
  def head(implicit ev : _0 < L) : A0 = s.unsized.head
  
  /**
   * Returns the tail of this collection. Available only if there is evidence that this collection has at least one
   * element.
   */
  def tail(implicit pred : Pred[L]) = wrap[Repr, pred.Out](s.unsized.tail)
  
  /**
   * Returns the first ''m'' elements of this collection. An explicit type argument must be provided. Available only if
   * there is evidence that this collection has at least ''m'' elements. The resulting collection will be statically
   * known to have ''m'' elements.
   */
  def take[M <: Nat](implicit diff : Diff[L, M], ev : ToInt[M]) = wrap[Repr, M](s.unsized.take(toInt[M]))
  
  /**
   * Returns the first ''m'' elements of this collection. Available only if there is evidence that this collection has
   * at least ''m'' elements. The resulting collection will be statically known to have ''m'' elements.
   */
  def take(m : Nat)(implicit diff : Diff[L, m.N], ev : ToInt[m.N]) = wrap[Repr, m.N](s.unsized.take(toInt[m.N]))

  /**
   * Returns all but the  first ''m'' elements of this collection. An explicit type argument must be provided. Available
   * only if there is evidence that this collection has at least ''m'' elements. The resulting collection will be 
   * statically known to have ''m'' less elements than this collection.
   */
  def drop[M <: Nat](implicit diff : Diff[L, M], ev : ToInt[M]) = wrap[Repr, diff.Out](s.unsized.drop(toInt[M]))
  
  /**
   * Returns all but the  first ''m'' elements of this collection. Available only if there is evidence that this
   * collection has at least ''m'' elements. The resulting collection will be statically known to have ''m'' less
   * elements than this collection.
   */
  def drop(m : Nat)(implicit diff : Diff[L, m.N], ev : ToInt[m.N]) = wrap[Repr, diff.Out](s.unsized.drop(toInt[m.N]))
  
  /**
   * Splits this collection at the ''mth'' element, returning the prefix and suffix as a pair. An explicit type argument
   * must be provided. Available only if there is evidence that this collection has at least ''m'' elements. The
   * resulting collections will be statically know to have ''m'' and ''n-m'' elements respectively.
   */
  def splitAt[M <: Nat](implicit diff : Diff[L, M], ev : ToInt[M]) = (take[M], drop[M])
  
  /**
   * Splits this collection at the ''mth'' element, returning the prefix and suffix as a pair. Available only if there
   * is evidence that this collection has at least ''m'' elements. The resulting collections will be statically know to
   * have ''m'' and ''n-m'' elements respectively.
   */
  def splitAt(m : Nat)(implicit diff : Diff[L, m.N], ev : ToInt[m.N]) = (take[m.N], drop[m.N])
  
  /**
   * Prepend the argument element to this collection. The resulting collection will be statically known to have a size
   * one greater than this collection.
   */
  def +:(elem : A0)(implicit cbf : BuildFrom[Repr, A0, Repr]) = {
    val builder = cbf.apply(s.unsized)
    builder += elem
    builder ++= s.unsized.toIterator
    wrap[Repr, Succ[L]](builder.result)
  }
  
  /**
   * Append the argument element to this collection. The resulting collection will be statically known to have a size
   * one greater than this collection.
   */
   def :+(elem : A0)(implicit cbf : BuildFrom[Repr, A0, Repr]) = {
    val builder = cbf.apply(s.unsized)
    builder ++= s.unsized.toIterator
    builder += elem
    wrap[Repr, Succ[L]](builder.result)
  }
  
  /**
   * Append the argument collection to this collection. The resulting collection will be statically known to have
   * ''m+n'' elements.
   */
  def ++[B >: A0, That, M <: Nat](that : Sized[That, M])
    (implicit
      sum : Sum[L, M],
      convThat : IsIterableLike[That] { type A = B },
      cbf : Factory[B, That],
      ev : AdditiveCollection[That]): Sized[That, sum.Out] = {
      val thisRepr: Repr = s.unsized
      val thatRepr: That = that.unsized
      val thisIter: Iterator[A0] = conv(thisRepr).toIterator
      val thatIter: Iterator[B] = convThat.conversion(thatRepr).toIterator
      val concd: Iterator[B] = thisIter ++ thatIter
      val blah: That = cbf.fromSpecific(concd)
      wrap[That, sum.Out](blah)
    }

  /**
   * Map across this collection. The resulting collection will be statically known to have the same number of elements
   * as this collection.
   */
  def map[B, That](f : A0 => B)(implicit cbf : BuildFrom[Repr, B, That], ev : AdditiveCollection[That]) = {
    val thisRepr: Repr = s.unsized
    val mapped: Iterable[B] = conv(thisRepr).view.map(f)
    val builder = cbf.newBuilder(thisRepr)
    builder.sizeHint(mapped)
    builder.addAll(mapped)
    val c: That = builder.result
    wrap[That, L](c)
  }

  /**
   * Converts this `Sized` to an `HList` whose elements have the same type as in `Repr`. 
   */
  def toHList(implicit hl: ToHList[Repr, L]): hl.Out = hl(s)

  /**
   * Converts this `Sized` to a tuple whose elements have the same type as in `Repr`.
   */
  def tupled[L0 <: HList, T](implicit hl: ToHList.Aux[Repr, L, L0], t: Tupler.Aux[L0, T]): T = t(hl(s))
}

trait LowPrioritySized {
  implicit def sizedToRepr[Repr](s : Sized[Repr, _]) : Repr = s.unsized
}

object Sized extends LowPrioritySized {
  implicit def sizedOps[Repr, L <: Nat](s : Sized[Repr, L])
    (implicit itl: IsIterableLike[Repr], ev: AdditiveCollection[Repr]): SizedOps[itl.A, Repr, L] =
      new SizedOps[itl.A, Repr, L](s, itl)
  
  def apply[CC[_]] = new SizedBuilder[CC]
  
  def apply[CC[_]]()
    (implicit dis: DefaultToIndexedSeq[CC], cbf : Factory[Nothing, CC[Nothing]], ev : AdditiveCollection[CC[Nothing]]) =
      new Sized[CC[Nothing], _0](cbf.newBuilder.result)
  
  def wrap[Repr, L <: Nat](r : Repr)(implicit ev : AdditiveCollection[Repr]) = new Sized[Repr, L](r)

  def unapplySeq[Repr, L <: Nat](x : Sized[Repr, L]) = Some(x.unsized)
}

/**
 * Evidence that `Repr` instances can be nested in a `Sized`.
 *
 * Should assert that a `Builder[_, Repr]` given n elements will result in a Repr of length n.
 *
 * @author Alexandre Archambault
 */
trait AdditiveCollection[Repr] extends Serializable

object AdditiveCollection {
  import scala.collection.immutable.Queue
  import scala.collection.LinearSeq

  implicit def linearSeqAdditiveCollection[T]: AdditiveCollection[LinearSeq[T]] =
    new AdditiveCollection[LinearSeq[T]] {}

  implicit def vectorAdditiveCollection[T]: AdditiveCollection[Vector[T]] =
    new AdditiveCollection[Vector[T]] {}

  implicit def arrayAdditiveCollection[T]: AdditiveCollection[Array[T]] =
    new AdditiveCollection[Array[T]] {}

  implicit def stringAdditiveCollection: AdditiveCollection[String] =
    new AdditiveCollection[String] {}

  implicit def listAdditiveCollection[T]: AdditiveCollection[List[T]] =
    new AdditiveCollection[List[T]] {}

  implicit def streamAdditiveCollection[T]: AdditiveCollection[Stream[T]] =
    new AdditiveCollection[Stream[T]] {}

  implicit def queueAdditiveCollection[T]: AdditiveCollection[Queue[T]] =
    new AdditiveCollection[Queue[T]] {}

  implicit def indexedSeqAdditiveCollection[T]: AdditiveCollection[IndexedSeq[T]] =
    new AdditiveCollection[IndexedSeq[T]] {}
}

class DefaultToIndexedSeq[CC[_]]
object DefaultToIndexedSeq {
  implicit def defaultInstance: DefaultToIndexedSeq[IndexedSeq] = null
  implicit def explicitInstance[CC[_]]: DefaultToIndexedSeq[CC] = null
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy