Skip to content

Commit

Permalink
#38, #43 - functor support for Arrow.Split:
Browse files Browse the repository at this point in the history
- defined generic `splitFunctor` implicit
- overriding default `Split` functor for `FuncArrow` with `FuncArrow.mapSplitToFunc1Arrow` (import is required)
  • Loading branch information
fehu committed Jul 11, 2018
1 parent 912236a commit 2ec0b9d
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 8 deletions.
35 changes: 35 additions & 0 deletions src/main/scala/com/abraxas/slothql/mapper/Arrow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,39 @@ object Functor {
compose: Arrow.Compose[ToF, ToG],
lowPriority: LowPriority
): Functor.Aux[From, To, compose.Out] = define[From, To](t => compose(fF(t.F), fG(t.G)))

implicit def splitFunctor[From <: Arrow, To <: Arrow, Arrows <: HList, Mapped <: HList](
implicit
isSplit: From <:< Arrow.Split[Arrows],
fmap: FMapHList.Aux[Arrows, To, Mapped],
split: Arrow.Split.Splitter[Mapped],
lowPriority: LowPriority
): Functor.Aux[From, To, split.Out] =
new Functor[From, To] {
type Out = split.Out
def apply(t: From): split.Out = split(fmap(t.arrows))
}


trait FMapHList[Arrows <: HList, To <: Arrow] extends DepFn1[Arrows] { type Out <: HList }
object FMapHList {
type Aux[Arrows <: HList, To <: Arrow, Mapped <: HList] = FMapHList[Arrows, To] { type Out = Mapped }
def apply[Arrows <: HList, To <: Arrow](implicit fmap: FMapHList[Arrows, To]): Aux[Arrows, To, fmap.Out] = fmap

implicit def fmapHnil[To <: Arrow]: FMapHList.Aux[HNil, To, HNil] = fmapHnilInstance.asInstanceOf[FMapHList.Aux[HNil, To, HNil]]
private lazy val fmapHnilInstance = new FMapHList[HNil, Arrow] {
type Out = HNil
def apply(t: HNil): HNil = HNil
}

implicit def fmapHcons[H <: Arrow, T <: HList, To <: Arrow](
implicit
mapH: Functor[H, To],
mapT: FMapHList[T, To]
): FMapHList.Aux[H :: T, To, mapH.Out :: mapT.Out] =
new FMapHList[H :: T, To] {
type Out = mapH.Out :: mapT.Out
def apply(t: H :: T): mapH.Out :: mapT.Out = mapH(t.head) :: mapT(t.tail)
}
}
}
34 changes: 29 additions & 5 deletions src/main/scala/com/abraxas/slothql/mapper/FuncArrow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,51 @@ import shapeless._
import shapeless.tag.@@

sealed trait FuncArrow extends Arrow
final class Func1Arrow[-S, +T](func: S => T) extends (S => T) with FuncArrow {
type Source >: S
type Target <: T
final class Func1Arrow[S, T](func: S => T) extends (S => T) with FuncArrow {
type Source = S
type Target = T
@inline def apply(s: S): T = func(s)
override def toString() = "<function1>"
}

object Func1Arrow {
type Aux[-S, +T] = Func1Arrow[_, _] { type Source >: S; type Target <: T }
def apply[S, T](func: S => T): Func1Arrow[S, T] = new Func1Arrow(func)

implicit def composeFunc1ArrowsAsFunctions[A, B, C]: Arrow.Compose.Aux[Func1Arrow[B, C], Func1Arrow[A, B], Func1Arrow[A, C]] =
composeInstance.asInstanceOf[Arrow.Compose.Aux[Func1Arrow[B, C], Func1Arrow[A, B], Func1Arrow[A, C]]]
private lazy val composeInstance = new Arrow.Compose[Func1Arrow[Any, Any], Func1Arrow[Any, Any]] {
type Out = Func1Arrow[Any, Any]
def apply(f: Func1Arrow[Any, Any], g: Func1Arrow[Any, Any]): Func1Arrow[Any, Any] = Func1Arrow(f compose g)
type Out = Func1Arrow.Aux[Any, Any]
def apply(f: Func1Arrow[Any, Any], g: Func1Arrow[Any, Any]): Func1Arrow.Aux[Any, Any] = Func1Arrow(f compose g)
}
}

object FuncArrow {
def apply[A <: Arrow](a: A)(implicit f: Functor[A, FuncArrow]): f.Out = f(a)


object ApplyTupled extends Poly1 {
implicit def applyFunc1Arrow[S, T]: Case.Aux[(Func1Arrow[S, T], S), T] = at[(Func1Arrow[S, T], S)]{ case (f, s) => f(s) }
}

// TODO: import is required
implicit def mapSplitToFunc1Arrow[
A <: Arrow, Arrows <: HList, A1 <: Arrow, Arrows1 <: HList, S, T, N <: Nat, ZL <: HList, TL <: HList
](
implicit
isSplit0: A <:< Arrow.Split[Arrows],
fmap: Functor.Aux[A, FuncArrow, A1],
isSplit1: A1 <:< Arrow.Split.Aux[Arrows1, S, T],
zip: ops.hlist.ZipConst.Aux[S, Arrows1, ZL],
applyArrows: ops.hlist.Mapper.Aux[ApplyTupled.type, ZL, TL],
tupler: ops.hlist.Tupler.Aux[TL, T]
): Functor.Aux[A, FuncArrow, Func1Arrow[S, T]] =
new Functor[A, FuncArrow] {
type Out = Func1Arrow[S, T]
def apply(t: A): Func1Arrow[S, T] = Func1Arrow(s => tupler(applyArrows(zip(s, isSplit1(fmap(t)).arrows)))
)
}

implicit def mapScalaExprIdToFunc1Arrow[A]: Functor.Aux[ScalaExpr.Id[A], FuncArrow, Func1Arrow[A, A]] =
scalaExprIdFunctor.asInstanceOf[Functor.Aux[ScalaExpr.Id[A], FuncArrow, Func1Arrow[A, A]]]
private lazy val scalaExprIdFunctor = new Functor[Arrow, FuncArrow] {
Expand Down
9 changes: 6 additions & 3 deletions src/test/scala/com/abraxas/slothql/TestArrows.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,12 @@ object FunctorsTest {
// ]{type Source = Book;type Target = (String, Option[Author], String)}
// = Split(SelectField(title) :: SelectField(author) :: SelectField(isbn) ∘ SelectField(meta) :: HNil)

// TODO ============================================================================================================
// val split1F = FuncArrow(split1)
// split1F(book)
// TODO: import is required
import FuncArrow.mapSplitToFunc1Arrow
val split1F = FuncArrow(split1)
// Func1Arrow[Book, (String, Option[Author], String)] = <function1>
split1F(book)
// (String, Option[Author], String) = ("History of Rome", Some(Author("Theodor Mommsen", None)), "9786610240531")

// val mapped0 = Functor.map(sel2 ∘ sel1).to[GraphPath]
// val mapped1 = Functor.map(sel3 ∘ (sel2 ∘ sel1)).to[GraphPath]
Expand Down

0 comments on commit 2ec0b9d

Please sign in to comment.