In Kotlin i can do
sealed class Substance {
object Uranus : Substance()
object Mercury: Substance()
object Ammonia : Substance()
}
data class DangerousBox<T : Substance>(val item: T)
fun main() {
val uranus = DangerousBox<Substance.Uranus>(Substance.Uranus)
val mercury: DangerousBox<Substance.Mercury> = uranus
}
Now i have invariant types DangerousBox<Substance.Uranus>, DangerousBox<Substance.Mercury> etc., so sample above would't compile.
How make it in Haskell?
Ideally i would like to have types
uranus :: DangerousBox Uranus
uranus = DangerousBox Mercury
and this sample musn't comlie too.
I tried to achieve it in the following ways:
- Modular constraint. I have module
module Sample.Types.Boxes
( Substance(..)
, dangerousBox
, VDangerousBox {- no Con-}
) where
data Substance
= Uranus
| Mercury
| Ammonia
deriving (Show)
data VDangerousBox a =
VDangerousBox Substance
deriving (Show)
dangerousBox :: Substance -> VDangerousBox Substance
dangerousBox a = VDangerousBox a
Clients of module can use only dangerousBox, but here i have jeneric in relation to Substance:
uranus :: VDangerousBox Substance
uranus = dangerousBox Uranus
- Data kinds
data DangerousBox :: Substance -> * where
DangerousBox :: Substance -> DangerousBox a
uranus :: DangerousBox Uranus
uranus = DangerousBox Uranus
mercury :: DangerousBox Mercury
mercury = uranus {- it's ok, it dosn't compile -}
But in this case the following case will compile:
mercury :: DangerousBox Mercury
mercury = DangerousBox Ammonia
I understand that i received equivalent for
data Box<T : Substance>(val value: Substance)
in term of Kotlin generics it.
It's no real case, it is only of "academic" interest.
Since, given a particular choice of substance, there isn't any other information to be contained in the box (at least in your Kotlin code), I'd first consider just omitting it entirely:
Then the
mercury = DangerousBox Ammoniaproblem can't even arise, because theDangerousBoxvalue constructor doesn't take any arguments.If for some reason you do need an actual value representing the tag-type, then this should not be the tag itself as a
Type-value, because in that viewUranus,MercuryandAmmoniaare all the same type. Instead, you should use what's normally called the singletons of these types. You could have them auto-generated, or define them manually:Now
UranusScorresponds to your KotlinSubstance.Uranuswhen appearing on the value-level. Your box type then becomes