9.11. Typy wyższego rzędu

Cecha StringInstance z pliku Kinds1.scala deklaruje jedną metodę o nazwie mkInstance. Zwróćmy uwagę na parametr typu tej cechy, który ma formę S[_]. Parametr S nie jest wobec tego parametrem oznaczającym jakiś zwykły typ, ale reprezentuje typ ogólny z jednym parametrem. Tego rodzaju parametry nazywać będziemy typami wyższego rzędu. Są one jedną z tych funkcjonalności języka Scala, których dotyczą flagi funkcjonalności (patrz punkt 13.5). Z tego powodu plik zawiera odpowiednią klauzulę importu umożliwiającą użycie typów wyższego rzędu bez generowania ostrzeżeń kompilacji.

Plik Kinds1.scala:
import language.higherKinds
trait StringInstance[S[_]] {
  def mkInstance(s: String): S[String]
}

Plik Kinds2.scala zawiera przykładową implementację cechy StringInstance. Obiekt ListInstance implementuje typ StringInstance[List]. Taki typ jest prawidłowy, gdyż typ ogólny List ma jeden parametr typu, a więc pasuje do parametru S[_].

Plik Kinds2.scala:
object ListInstance extends StringInstance[List] {
  def mkInstance(s: String): List[String] = List(s)
}

Metoda mkInstance tego obiektu tworzy jednoelementowe listy zawierające element typu String.

scala> ListInstance.mkInstance("a")
res0: List[String] = List(a)

Plik Kinds3.scala zawiera implementację typu StringInstance[Option].

Plik Kinds3.scala:
object OptionInstance extends StringInstance[Option] {
  def mkInstance(s: String): Option[String] = Some(s)
}

Również ta implementacja jest prawidłowa, gdyż typ ogólny Option ma jeden parametr typu.

scala> OptionInstance.mkInstance("a")
res1: Option[String] = Some(a)

Kolejny przykład pokazuje nieudaną próbę użycia typu ogólnego StringInstance. Definicja cechy IntInstance jest nieprawidłowa, dlatego że próbuje rozszerzyć StringInstance[Int], który nie jest prawidłowym typem, gdyż Int nie jest typem ogólnym, a parametrem StringInstance musi być typ ogólny z jednym parametrem typu.

scala> trait IntInstanceTrait extends StringInstance[Int]
<console>:10: error: Int takes no type parameters, expected: one
       trait IntInstanceTrait extends StringInstance[Int]
                                                     ^

Definicja cechy EitherInstance z następnego przykładu również jest nieprawidłowa, gdyż próbuje rozszerzać StringInstance[Either]. Either jest co prawda typem ogólnym, ale takim, który ma dwa parametry typu, a nie jeden, wymagany przez cechę StringInstance.

scala> trait EitherInstanceTrait extends StringInstance[Either]
<console>:10: error: Either takes two type parameters, expected: one
       trait EitherInstanceTrait extends StringInstance[Either]
                                                        ^

W poniższym przykładzie tworzony jest alias typu ogólnego Either z ustaloną wartością pierwszego parametru. Typ EitherInt jest odpowiednikiem typu Either mającego w pierwszym parametrze ustawiony typ Int.

scala> type EitherInt[T] = Either[Int,T]
defined type alias EitherInt

Skoro EitherInt ma jeden parametr, to może być użyty w poniższym przykładzie definiującym kolejny typ rozszerzający StringInstance.

scala> trait EitherIntInstanceTrait extends StringInstance[EitherInt]
defined trait EitherIntInstanceTrait

Zamiast definiować EitherInt osobno, w przykładzie z pliku Kinds4.scala definiujemy go w miejscu, w którym jest nam potrzebny i od razu używamy, wykorzystując notację odwołania do składowej będącej typem.

Plik Kinds4.scala:
object EitherIntInstance
extends StringInstance[({type EitherInt[T]=Either[Int,T]})#EitherInt] {
  def mkInstance(s: String): Either[Int,String] = Right(s)
}

Obiekt EitherIntInstance możemy teraz wykorzystać tak jak w poniższym przykładzie.

scala> EitherIntInstance.mkInstance("a")
res2: Either[Int,String] = Right(a)

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.