?


Alan Kay
предлага термина ООП
(c. 1967)
“I made up the term ‘object-oriented’, and I can tell you I didn’t have C++ in mind.” – Alan Kay
“I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages… OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.” – Alan Kay
Забележете, че не се споменават класове, наследяване и др.
“So: both OOP and functional computation can be completely compatible (and should be!). There is no reason to munge state in objects, and there is no reason to invent “monads” in FP. We just have to realize that “computers are simulators” and figure out what to simulate.” – Alan Kay
ООП във функционален език:
Да дефинираме клас Rational
apply методиВсеки обект с apply метод може да бъде използван като
функция:
apply методиclass Rational:
// ...
object Rational:
val Zero = Rational(0) // използва apply, дефиниран долу
def apply(n: Int, d: Int = 1) = new Rational(n, d)
def sum(rationals: Rational*): Rational =
if rationals.isEmpty then Zero
else rationals.head + sum(rationals.tail)
Rational.sum(Rational(1, 2), Rational(5), Rational(3, 5)) // не е нужно да пишем newList(1, 2, 3) се свежда до
List.apply(1, 2, 3),
което е функция с променлив брой
параметри
Имат достъп и до private/protected членовете:
implicit def intToRational(n: Int): Rational = Rational(n)
Rational(2, 3) + 1 // Rational(5, 3)
1 + Rational(2, 3) // Rational(5, 3), също работиПреобразува се до:
Когато компилаторът не открие метод с очакваните име и
параметри
решава да потърси за възможна имплицитна конверсия към
тип,
който има този метод
import scala.language.implicitConversionsCtrl/Cmd + Alt + Shift + Xval полетапридружаващ обект с apply
equals, hashCode,
toString
copy – позволява инстанциране на
нова версия, базирана на съществуващата
още няколко удобства – за тях по-натам
В Scala 3 автоматично се генерира придружаващ обект с apply за всеки клас (не само за case класовете):
UAC – интерфейсът не се променя от това дали дадено име е
имплементирано чрез ичисление (def)
или чрез съхранена
стойност (val)
Могат да са във всеки scope, не е нужно да са в началото на файла:
Автоматично във всеки файл се включват следните import-и:
Позволяват делегация:
object IntUtils:
def twice(n: Int): Int = 2 * n
def squared(n: Int): Int = n * n
object DoubleUtils:
def twice(n: Double): Double = 2 * n
def squared(n: Double): Double = n * n
object MathUtils:
export IntUtils.*
export DoubleUtils.*
MathUtils.twice(2) // 4
MathUtils.twice(2.0) // 4.0importclass Scanner:
def scan(image: Image): Page = ???
def isOn: Boolean = ???
class Printer:
def print(page: Page): Image = ???
def isOn: Boolean = ???
class Copier:
private val scanner = new Scanner
private val printer = new Printer
export scanner.scan
export printer.print
def isOn = scanner.isOn && printer.isOn
val copier = new Copier
val image = ???
val copiedImage = copier.print(copier.scan(image))
image == copiedImage // true, hopefully :DМогат да бъдат overload-вани,
import-ват се по името на
метода:
implicit class EnrichedInt(val n: Int) extends AnyVal:
def squared = n * n
def **(exp: Double) = math.pow(n, exp)
3.squared // 9
2 ** 3 // 8.0Тук не е нужен
import scala.language.implicitConversions
opaque type PersonId = String
object PersonId:
def apply(id: String): PersonId = id
extension (personId: PersonId) def value: String = personId
opaque type LocationId = String
object LocationId:
def apply(id: String): LocationId = id
extension (locationId: LocationId) def value: String = locationId
def createAddressRegistration(person: PersonId, location: LocationId) = ???PersonId и
LocationId като напълно различни типове
StringAnyVal
класовеcase class Eagle(name: String):
def flyThrough(location: String): String =
s"Hi, I am old $name and I am looking for food at $location."
case class Owl(age: Int):
def flyThrough(location: String): String =
s"Hi, I am a $age years old owl and I am flying through $location. Hoot, hoot!"Scala 3 добавя сечение (&) и обединение
(|) на типове
&)trait LovingAnimal:
def name: String
def hug = s"A hug from $name"
case class Owl(name: String, age: Int):
def flyThrough(location: String): String =
s"Hi, I am a $age years old owl and I am flying through $location. Hoot, hoot!"
val lovelyOwl: Owl & LovingAnimal = new Owl("Oliver", 7) with LovingAnimal
lovelyOwl.hug // A hug from Oliver
lovelyOwl.flyThrough("Plovdiv") // Hi, I am a 7 years old owl and
// I am flying through Plovdiv. Hoot, hoot!|)|)def toInteger(value: String | Int | Double): Int = value match
case n: Int => n
case s: String => s.toInt|def toInteger(value: String | Int | Double): Int = value match
| ^^^^^
| match may not be exhaustive.
|
| It would fail on pattern case: _: Double
|)The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).
trait Shape
case class Circle(r: Double) extends Shape
case class Rectangle(a: Double, b: Double) extends Shape
def area(s: Shape): Double = s match
case Circle(r) => math.Pi * r * r
case Rectangle(a, b) => a * bcase класовете могат да бъдат използвани в pattern
matching
case class Square(a: Double) extends Shape
def area(s: Shape): Double = s match
case Circle(r) => math.Pi * r * r
case Rectangle(a, b) => a * b
case Square(a) => a * a
def circumference(s: Shape): Double = s match
case Circle(r) => 2 * math.Pi * r
case Rectangle(a, b) => 2 * (a + b)
case Square(a) => 4 * adef buyTea(cc: CreditCard, paymentService: PaymentService): Tea =
val teaCup = new Tea(...)
paymentService.charge(cc, teaCup.price)
teaCupОтлагане на страничния ефект =>
Charge обект)купуване на n кафета и събиране на Charge-ове
анализ на Charge-ове от различни потребители