On Type Level, i stumble upon the following:
sealed abstract class StSource[A] {
type S
def init: S // create the initial state
def emit(s: S): (A, S) // emit a value, and update state
}
object StSource {
type Aux[A, S0] = StSource[A] {type S = S0}
def apply[A, S0](i: S0)(f: S0 => (A, S0)): Aux[A, S0] =
new StSource[A] {
type S = S0
def init = i
def emit(s: S0) = f(s)
}
}
The line that intrigued me is type Aux[A, S0] = StSource[A] {type S = S0}
In paerticular {type S = S0}
in StSource[A] {type S = S0}
I do not really know how to read this, as in interpreting the construct involved here.
What is StSource[A] {type S = S0}
??? is that a structural type (part of it looks like it)
When defining type like trait, or class, Is the body of a class part of the type constructor represented by the class itself? what happened to the method in it ?
Really confused. Can someone deconstruct that please ?
StSource[A] {type S = S0}
is a refined type.{type S = S0}
is a type refinement.From one side,
StSource[A] {type S = S0}
is a subtype ofStSource[A]
.From the other side,
StSource[A]
is also an existential type with respect toStSource[A] {type S = S0}
, namelyStSource[A]
isStSource.Aux[A, _]
(akaStSource.Aux[A, X] forSome {type X}
).https://scala-lang.org/files/archive/spec/2.13/03-types.html#compound-types
See also examples how to use refined types:
https://typelevel.org/blog/2015/07/19/forget-refinement-aux.html
How can I have a method parameter with type dependent on an implicit parameter?
When are dependent types needed in Shapeless?
Why is the Aux technique required for type-level computations?
Understanding the Aux pattern in Scala Type System
Enforcing that dependent return type must implement typeclass
You can replace
aka
with
but there is no sense in that because the type remains the same
When you add
type S = S0
to the type you provide additional information (that typeS
is specific) but when you adddef init: S
,def emit(s: S): (A, S)
to the type you don't provide additional information (methodsinit
,emit
being there is clear from the definition of classStSource[A]
).Other situation would be if the class were defined as just
or even
Then
would be a type different from
StSource[A]
orStSource[A] {type S = S0}
(a subtype of them). It would be a structural type (existence ofinit
,emit
would be checked using runtime reflection). Hereis a refinement but not type refinement.
Unlike
def
s (init
,emit
) type members don't have runtime representation (unless you persist them, e.g. withTypeTag
s) so using type refinement doesn't have runtime overhead.