Scala 3: livrar-se do implícito. Typeclasses







Meu artigo anterior foi sobre conversões implícitas e métodos de extensão. Neste artigo, discutiremos a nova maneira de declarar typeclasses no Scala 3.







Tendo aprendido como adicionar métodos externos a classes arbitrárias, queremos ir ainda mais fundo, ou seja, aprender como converter classes arbitrárias em interfaces "externas", isto é, sem herdar diretamente delas. Esta tarefa é resolvida por typeclasses.







Mas primeiro, vamos descobrir o que é uma typeclass. Como o próprio conceito, o termo " classe de tipo" originou-se em Haskell. A palavra "classe" é usada aqui não no sentido restrito que é aceito em OOP, mas em um sentido mais amplo - como uma designação de um conjunto de entidades que têm algo em comum. (Eu entendo que a maioria das pessoas que irão ler este artigo tem um histórico de OOP, e para eles o termo "typeclass" soa como "óleo de óleos", embora signifique "categoria de óleos". Para evitar confusão com classes OOP convencionais , em vez de "classe de tipo", usarei apenas a transliteração "classe de letras" - transl.







A sintaxe dos exemplos está atualizada Scala 3.0.0-M3



.

, , , . Scala 3:







// Adapted from this Dotty documentation:
// https://dotty.epfl.ch/docs/reference/contextual/type-classes.html

trait Semigroup[T]:
  extension (t: T)
    def combine(other: T): T
    def <+>(other: T): T = t.combine(other)

trait Monoid[T] extends Semigroup[T]:
  def unit: T
      
      





, <+>



. — , , 0 — . , Semigroup



Monoid



.







Semigroup



T



extension- combine



<+>



, combine



. unit



Monoid



, extension-. , unit



T



, , , T



, .







:







given StringMonoid: Monoid[String] with
  def unit: String = ""
  extension (s: String) def combine(other: String): String = s + other

given IntMonoid: Monoid[Int] with
  def unit: Int = 0
  extension (i: Int) def combine(other: Int): Int = i + other
      
      





. , given foo: Bar



— implicit-. Scala3 REPL, , : StringMonoid



IntMonoid



.







- :







"2" <+> ("3" <+> "4")             // "234"
("2" <+> "3") <+> "4"             // "234"
StringMonoid.unit <+> "2"         // "2"
"2" <+> StringMonoid.unit         // "2"

2 <+> (3 <+> 4)                   // 9
(2 <+> 3) <+> 4                   // 9
IntMonoid.unit <+> 2              // 2
2 <+> IntMonoid.unit              // 2
      
      





StringMonoid



IntMonoid



unit



. <+>



extension-, String



Int



. <+>



, .







: given Monoid[String] with ...



. unit



summon[Monoid[String]]



. summon



implicitly



, implicit- . given_Monoid_String



, , .







, , - ( unit



). .







, . , , IntMonoid



Numeric[T]



:







given NumericMonoid[T](using num: Numeric[T]): Monoid[T] with
  def unit: T = num.zero
  extension (t: T) def combine(other: T): T = num.plus(t, other)

2.2 <+> (3.3 <+> 4.4)             // 9.9
(2.2 <+> 3.3) <+> 4.4             // 9.9

BigDecimal(3.14) <+> NumericMonoid.unit
NumericMonoid[BigDecimal].unit  <+> BigDecimal(3.14)
      
      





using



, Scala 2 implicit



. .







. NumericMonoid



— , Monoid[T]



— . T



, . NumericMonoid[BigDecimal]



, NumericMonoid



BigDecimal



. num



NumericMonoid



, using



.







, unit



. -, <+>



. Scala obj1.method(obj2)



.







?



using



.








All Articles