Assuming we have a type T[A,B]
, can we express the following type constraint, let's call it HT
:
every type satisfying HT
must be of form T[P1, P2] :: T[P2, P3] :: T[P3, P4] :: ... :: T[PN-1, PN] :: HNil
for some type x = P1 :: P2 :: ... :: PN :: HNil
.
I am trying to find an abstraction over a typed sequential processing pipeline.
Abstraction can be of two types, namely, data abstraction and control abstraction. Data abstraction means hiding the details about the data and control abstraction means hiding the implementation details. In object-oriented approach, one can abstract both data and functions.
Data abstraction is the reduction of a particular body of data to a simplified representation of the whole. Abstraction, in general, is the process of removing characteristics from something to reduce it to a set of essential elements.
In object-oriented programming, abstraction is one of three central principles (along with encapsulation and inheritance). Through the process of abstraction, a programmer hides all but the relevant data about an object in order to reduce complexity and increase efficiency.
An abstraction barrier violation occurs whenever a part of the program that can use a higher level function instead uses a function in a lower level.
The most convenient way to do this kind of thing is usually to write your own type class. Here's a quick working sketch:
import shapeless._
trait T[I, O] extends (I => O)
trait Pipeline[P <: HList] {
type In
type Out
type Values <: HList
}
object Pipeline {
type Aux[P <: HList, In0, Out0, Values0 <: HList] = Pipeline[P] {
type In = In0; type Out = Out0; type Values = Values0
}
def apply[P <: HList](
implicit pipeline: Pipeline[P]
): Aux[P, pipeline.In, pipeline.Out, pipeline.Values] = pipeline
implicit def onePipeline[I, O]: Aux[T[I, O] :: HNil, I, O, I :: O :: HNil] =
new Pipeline[T[I, O] :: HNil] {
type In = I
type Out = O
type Values = I :: O :: HNil
}
implicit def longerPipeline[I, O, P <: HList, Out0, Values0 <: HList](
implicit pipeline: Aux[P, O, Out0, Values0]
): Aux[T[I, O] :: P, I, Out0, I :: Values0] =
new Pipeline[T[I, O] :: P] {
type In = I
type Out = Out0
type Values = I :: Values0
}
}
And then (reformatted for clarity):
scala> Pipeline[T[String, Int] :: T[Int, Char] :: HNil]
res5: Pipeline[T[String, Int] :: T[Int, Char] :: HNil] {
type In = String
type Out = Char
type Values = String :: Int :: Char :: HNil
} = Pipeline$$anon$2@38fd077c
scala> Pipeline[T[String, Int] :: T[Char, Char] :: HNil]
<console>:19: error: could not find implicit value for parameter
pipeline: Pipeline[[T[String, Int] :: T[Char, Char] :: HNil]
Pipeline[T[String, Int] :: T[Char, Char] :: HNil]
^
The invalid pipeline doesn't compile, and for the valid one we get the endpoints and intermediate values correctly inferred.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With