Home > Blockchain >  What is this Scala syntax?
What is this Scala syntax?

Time:01-31

Take a look at this:

/** takes a Spellbook and returns a Spellbook guaranteeing 
  * that all spells have been loaded from the database. */
 def checkIfSpellsLoaded[S <: Spellbook](spellbook :S) :Option[S { type SpellsLoaded }] =
    if (spellbook.spellsLoaded) Some(spellbook.asInstanceOf[S { type SpellsLoaded }])
    else None
 def checkIfOwnerLoaded[S <: Spellbook](spellbook :S) :Option[S { type OwnerLoaded }] =
     if (spellbook.ownerLoaded) Some(spellbook.asInstanceOf[S { type OwnerLoaded }])
     else None

What is that { type X } doing as part of a type parameter?? What is going on here?

CodePudding user response:

In Scala class members can be def, val and (relevant for us) type

https://docs.scala-lang.org/tour/abstract-type-members.html

https://typelevel.org/blog/2015/07/13/type-members-parameters.html

Scala: Abstract types vs generics

How to work with abstract type members in Scala

Type members are used to create path-dependent types

What is meant by Scala's path-dependent types?

https://docs.scala-lang.org/scala3/book/types-dependent-function.html

If Spellbook has type members SpellsLoaded, OwnerLoaded

trait Spellbook {
  type SpellsLoaded
  type OwnerLoaded

  def spellsLoaded: Boolean
  def ownerLoaded: Boolean
}

then for S <: Spellbook the types S, S { type SpellsLoaded } and S { type OwnerLoaded } are the same

type S <: Spellbook

implicitly[(S { type SpellsLoaded }) =:= S] // compiles
implicitly[S =:= (S { type SpellsLoaded })] // compiles
implicitly[(S { type OwnerLoaded }) =:= S]  // compiles
implicitly[S =:= (S { type OwnerLoaded })]  // compiles

But if Spellbook doesn't have type members SpellsLoaded, OwnerLoaded

trait Spellbook {
  // no SpellsLoaded, OwnerLoaded

  def spellsLoaded: Boolean
  def ownerLoaded: Boolean
}

then the refined types S { type SpellsLoaded } and S { type OwnerLoaded } are just subtypes of S (having those type members)

implicitly[(S { type SpellsLoaded }) <:< S] // compiles
// implicitly[S <:< (S { type SpellsLoaded })] // doesn't compile
implicitly[(S { type OwnerLoaded }) <:< S] // compiles
// implicitly[S <:< (S { type OwnerLoaded })] // doesn't compile

and the refined types S { type SpellsLoaded = ... } and S { type OwnerLoaded = ... } in their turn are subtypes of the former refined types

implicitly[(S {type SpellsLoaded = String}) <:< (S {type SpellsLoaded})] // compiles
// implicitly[(S {type SpellsLoaded}) <:< (S {type SpellsLoaded = String})] // doesn't compile
implicitly[(S {type OwnerLoaded = Int}) <:< (S {type OwnerLoaded})] // compiles
// implicitly[(S {type OwnerLoaded}) <:< (S {type OwnerLoaded = Int})] // doesn't compile

S { type SpellsLoaded } and S { type OwnerLoaded } are shorthands for S { type SpellsLoaded >: Nothing <: Any } and S { type OwnerLoaded >: Nothing <: Any } while S { type SpellsLoaded = SL } and S { type OwnerLoaded = OL } are shorthands for S { type SpellsLoaded >: SL <: SL } and S { type OwnerLoaded >: OL <: OL }.

Casting .asInstanceOf[S { type SpellsLoaded }], .asInstanceOf[S { type OwnerLoaded }] looks like SpellsLoaded, OwnerLoaded are used as phantom types

https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:labelled-generic:type-tagging (5.2 Type tagging and phantom types)

So you seem to encode in types that the methods checkIfSpellsLoaded, checkIfOwnerLoaded were applied to S.

See also

Confusion about type refinement syntax

What is a difference between refinement type and anonymous subclass in Scala 3?

  • Related