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

quasar.std.set.scala Maven / Gradle / Ivy

There is a newer version: 28.1.6
Show newest version
/*
 * Copyright 2014–2016 SlamData Inc.
 *
 * 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 quasar.std

import quasar.Predef._
import quasar.fp._
import quasar._, LogicalPlan._

import scala.collection.immutable.NumericRange

import matryoshka._, Recursive.ops._
import scalaz._, Scalaz._, Validation.success
import shapeless.{Data => _, _}

trait SetLib extends Library {
  val Take = BinaryFunc(
    Sifting,
    "(LIMIT)",
    "Takes the first N elements from a set",
    Type.Top,
    Func.Input2(Type.Top, Type.Int),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(_, Type.Const(Data.Int(n))) if n == 0 =>
        Type.Const(Data.Set(Nil))
      case Sized(Type.Const(Data.Set(s)), Type.Const(Data.Int(n)))
          if n.isValidInt =>
        Type.Const(Data.Set(s.take(n.intValue)))
      case Sized(t, _) => t
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Int))))

  val Drop = BinaryFunc(
    Sifting,
    "(OFFSET)",
    "Drops the first N elements from a set",
    Type.Top,
    Func.Input2(Type.Top, Type.Int),
    new Func.Simplifier {
      def apply[T[_[_]]: Recursive: Corecursive](orig: LogicalPlan[T[LogicalPlan]]) = orig match {
        case InvokeF(_, Sized(Embed(set), Embed(ConstantF(Data.Int(n)))))
            if n == 0 =>
          set.some
        case _ => None
      }
    },
    partialTyper[nat._2] {
      case Sized(Type.Const(Data.Set(s)), Type.Const(Data.Int(n)))
          if n.isValidInt =>
        Type.Const(Data.Set(s.drop(n.intValue)))
      case Sized(t, _) => t
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Int))))

  val Range = BinaryFunc(
    Expansion,
    "(..)",
    "Creates a set of values in the range from `a` to `b`, inclusive.",
    Type.Int,
    Func.Input2(Type.Int, Type.Int),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(Type.Const(Data.Int(a)), Type.Const(Data.Int(b))) =>
        Type.Const(
          if (a ≟ b) Data.Int(a)
          else Data.Set(NumericRange.inclusive[BigInt](a, b, 1).toList ∘ (Data.Int(_))))
      case Sized(_, _) => Type.Int
    },
    basicUntyper)

  val OrderBy = TernaryFunc(
    Sifting,
    "ORDER BY",
    "Orders a set by the natural ordering of a projection on the set",
    Type.Top,
    Func.Input3(Type.Top, Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._3] {
      case Sized(set, _, _) => set
    },
    untyper[nat._3](t => success(Func.Input3(t, Type.Top, Type.Top))))

  val Filter = BinaryFunc(
    Sifting,
    "WHERE",
    "Filters a set to include only elements where a projection is true",
    Type.Top,
    Func.Input2(Type.Top, Type.Bool),
    new Func.Simplifier {
      def apply[T[_[_]]: Recursive: Corecursive](orig: LogicalPlan[T[LogicalPlan]]) =
        orig match {
          case InvokeF(_, Sized(Embed(set), Embed(ConstantF(Data.True)))) =>
            set.some
          case _ => None
        }
    },
    partialTyper[nat._2] {
      case Sized(_ , Type.Const(Data.False)) => Type.Const(Data.Set(Nil))
      case Sized(set, _) => set
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Bool))))

  object JoinFunc {
    def unapply(func: TernaryFunc): Option[TernaryFunc] =
      func match {
        case (InnerJoin | LeftOuterJoin | RightOuterJoin | FullOuterJoin) => Some(func)
        case _ => None
      }
  }

  val InnerJoin = TernaryFunc(
    Transformation,
    "INNER JOIN",
    "Returns a new set containing the pairs values from the two sets that satisfy the condition.",
    Type.Top,
    Func.Input3(Type.Top, Type.Top, Type.Bool),
    noSimplification,
    partialTyper[nat._3] {
      case Sized(_, _, Type.Const(Data.Bool(false))) => Type.Const(Data.Set(Nil))
      case Sized(Type.Const(Data.Set(Nil)), _, _) => Type.Const(Data.Set(Nil))
      case Sized(_, Type.Const(Data.Set(Nil)), _) => Type.Const(Data.Set(Nil))
      case Sized(s1, s2, _) => Type.Obj(Map("left" -> s1, "right" -> s2), None)
    },
    untyper[nat._3](t =>
      (t.objectField(Type.Const(Data.Str("left"))) |@| t.objectField(Type.Const(Data.Str("right"))))((l, r) =>
        Func.Input3(l, r, Type.Bool))))

  val LeftOuterJoin = TernaryFunc(
    Transformation,
    "LEFT OUTER JOIN",
    "Returns a new set containing the pairs values from the two sets that satisfy the condition, plus all other values from the left set.",
    Type.Top,
    Func.Input3(Type.Top, Type.Top, Type.Bool),
    noSimplification,
    partialTyper[nat._3] {
      case Sized(s1, _, Type.Const(Data.Bool(false))) =>
        Type.Obj(Map("left" -> s1, "right" -> Type.Null), None)
      case Sized(Type.Const(Data.Set(Nil)), _, _) => Type.Const(Data.Set(Nil))
      case Sized(s1, s2, _) =>
        Type.Obj(Map("left" -> s1, "right" -> (s2 ⨿ Type.Null)), None)
    },
    untyper[nat._3](t =>
      (t.objectField(Type.Const(Data.Str("left"))) |@| t.objectField(Type.Const(Data.Str("right"))))((l, r) =>
        Func.Input3(l, r, Type.Bool))))

  val RightOuterJoin = TernaryFunc(
    Transformation,
    "RIGHT OUTER JOIN",
    "Returns a new set containing the pairs values from the two sets that satisfy the condition, plus all other values from the right set.",
    Type.Top,
    Func.Input3(Type.Top, Type.Top, Type.Bool),
    noSimplification,
    partialTyper[nat._3] {
      case Sized(_, s2, Type.Const(Data.Bool(false))) =>
        Type.Obj(Map("left" -> Type.Null, "right" -> s2), None)
      case Sized(_, Type.Const(Data.Set(Nil)), _) => Type.Const(Data.Set(Nil))
      case Sized(s1, s2, _) => Type.Obj(Map("left" -> (s1 ⨿ Type.Null), "right" -> s2), None)
    },
    untyper[nat._3](t =>
      (t.objectField(Type.Const(Data.Str("left"))) |@| t.objectField(Type.Const(Data.Str("right"))))((l, r) =>
        Func.Input3(l, r, Type.Bool))))

  val FullOuterJoin = TernaryFunc(
    Transformation,
    "FULL OUTER JOIN",
    "Returns a new set containing the pairs values from the two sets that satisfy the condition, plus all other values from either set.",
    Type.Top,
    Func.Input3(Type.Top, Type.Top, Type.Bool),
    noSimplification,
    partialTyper[nat._3] {
      case Sized(Type.Const(Data.Set(Nil)), Type.Const(Data.Set(Nil)), _) =>
        Type.Const(Data.Set(Nil))
      case Sized(s1, s2, _) =>
        Type.Obj(Map("left" -> (s1 ⨿ Type.Null), "right" -> (s2 ⨿ Type.Null)), None)
    },
    untyper[nat._3](t =>
      (t.objectField(Type.Const(Data.Str("left"))) |@| t.objectField(Type.Const(Data.Str("right"))))((l, r) =>
        Func.Input3(l, r, Type.Bool))))

  val GroupBy = BinaryFunc(
    Transformation,
    "GROUP BY",
    "Groups a projection of a set by another projection",
    Type.Top,
    Func.Input2(Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(s1, _) => s1
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Top))))

  val Distinct = UnaryFunc(
    Sifting,
    "DISTINCT",
    "Discards all but the first instance of each unique value",
    Type.Top,
    Func.Input1(Type.Top),
    noSimplification,
    partialTyper[nat._1] {
      case Sized(a) => a
    },
    untyper[nat._1](t => success(Func.Input1(t))))

  val DistinctBy = BinaryFunc(
    Sifting,
    "DISTINCT BY",
    "Discards all but the first instance of the first argument, based on uniqueness of the second argument",
    Type.Top,
    Func.Input2(Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(a, _) => a
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Top))))

  val Union = BinaryFunc(
    Transformation,
    "(UNION ALL)",
    "Creates a new set with all the elements of each input set, keeping duplicates.",
    Type.Top,
    Func.Input2(Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(Type.Const(Data.Set(Nil)), s2) => s2
      case Sized(s1, Type.Const(Data.Set(Nil))) => s1
      case Sized(s1, s2)                        => s1 ⨿ s2
    },
    untyper[nat._2](t => success(Func.Input2(t, t))))

  val Intersect = BinaryFunc(
    Transformation,
    "(INTERSECT ALL)",
    "Creates a new set with only the elements that exist in both input sets, keeping duplicates.",
    Type.Top,
    Func.Input2(Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(s1, s2) => if (s1 == s2) s1 else Type.Const(Data.Set(Nil))
    },
    untyper[nat._2](t => success(Func.Input2(t, t))))

  val Except = BinaryFunc(
    Transformation,
    "(EXCEPT)",
    "Removes the elements of the second set from the first set.",
    Type.Top,
    Func.Input2(Type.Top, Type.Top),
    new Func.Simplifier {
      def apply[T[_[_]]: Recursive: Corecursive](orig: LogicalPlan[T[LogicalPlan]]) = orig match {
        case InvokeF(_, Sized(Embed(set), Embed(ConstantF(Data.Set(Nil))))) =>
          set.some
        case _ => None
      }
    },
    partialTyper[nat._2] {
      case Sized(s1, _) => s1
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Top))))

  // TODO: Handle “normal” functions without creating Funcs. They should be in
  //       a separate functor and inlined prior to getting this far. It will
  //       also allow us to make simplification non-Corecursive and ∴ operate
  //       on Cofree.
  val In = BinaryFunc(
    Mapping,
    "(in)",
    "Determines whether a value is in a given set.",
    Type.Bool,
    Func.Input2(Type.Top, Type.Top),
    new Func.Simplifier {
      def apply[T[_[_]]: Recursive: Corecursive](orig: LogicalPlan[T[LogicalPlan]]) =
        orig match {
          case InvokeF(_, Sized(item, set)) => set.project match {
            case ConstantF(Data.Set(_)) => Within(item, StructuralLib.UnshiftArray(set).embed).some
            case ConstantF(_)           => RelationsLib.Eq(item, set).some
            case lp                     => Within(item, StructuralLib.UnshiftArray(set).embed).some
          }
          case _ => None
        }
    },
    partialTyper[nat._2] {
      case Sized(_, Type.Const(Data.Set(Nil))) =>
        Type.Const(Data.Bool(false))
      case Sized(Type.Const(x), Type.Const(Data.Set(set))) =>
        Type.Const(Data.Bool(set.contains(x)))
      case Sized(Type.Const(x), Type.Const(y)) =>
        Type.Const(Data.Bool(x == y))
      case Sized(_, _) => Type.Bool
    },
    basicUntyper)

  val Within = BinaryFunc(
    Mapping,
    "within",
    "Determines whether a value is in a given array.",
    Type.Bool,
    Func.Input2(Type.Top, Type.AnyArray),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(_, Type.Const(Data.Arr(Nil))) =>
        Type.Const(Data.Bool(false))
      case Sized(Type.Const(x), Type.Const(Data.Arr(arr))) =>
        Type.Const(Data.Bool(arr.contains(x)))
      case Sized(_, _) => Type.Bool
    },
    basicUntyper)

  val Constantly = BinaryFunc(
    Mapping,
    "CONSTANTLY", "Always return the same value",
    Type.Bottom,
    Func.Input2(Type.Top, Type.Top),
    noSimplification,
    partialTyper[nat._2] {
      case Sized(Type.Const(const), Type.Const(Data.Set(s))) =>
        Type.Const(Data.Set(s.map(κ(const))))
      case Sized(const, _) => const
    },
    untyper[nat._2](t => success(Func.Input2(t, Type.Top))))

  def unaryFunctions: List[GenericFunc[nat._1]] =
    Distinct :: Nil

  def binaryFunctions: List[GenericFunc[nat._2]] =
    Take :: Drop :: Range :: Filter :: GroupBy :: DistinctBy ::
    Union :: Intersect :: Except :: In :: Within :: Constantly :: Nil

  def ternaryFunctions: List[GenericFunc[nat._3]] =
    OrderBy :: InnerJoin :: LeftOuterJoin :: RightOuterJoin :: FullOuterJoin :: Nil
}

object SetLib extends SetLib




© 2015 - 2025 Weber Informatics LLC | Privacy Policy