Scala:
val x: Int = 1
Java:
Integer x = 1;
C#:
int i = 1;
Тип это некоторое ограничение, накладываемое на какое-то значение и на то как это значение может использоваться.
val a: Int = 1
a > 1
a = false
val b: Boolean = false
b || true
b > 1
b.someMethod()
Система типов в языке - это набор базовых типов, правила и механизмы по описанию новых типов и по применению различных типов.
Примеры: Java, C#, TypeScript, C/C++, Rust, Scala и др.
Примеры: JavaScript, Python, PHP, Ruby, Perl, Clojure и др.
(scala.Dynamic)
(JavaScript - Flow/TypeScript, Python - PEP 484 introduced type hints)
Java, C#, C++, Scala, Python и др.
JavaScrip, PHP и др.
Java, C#, C++, Scala и др.
JavaScrip, PHP и др.
Большинство программистов считают что в большинстве случаев лучше язык со статической и строгой типизацией при разработке программ
Ниша динамических и/или слабо типизированных языков - небольших и простые приложения (скрипты, примитивные приложения, обучение)
Проблемы для языка:
Проблемы для разработчика:
Решение проблем разработчиков в Scala:
(однако создателям библиотек приходиться попотеть)
((также как и создателям языка))
По простому - там где типы могут быть однозначно просчитаны компилятором - их можно не писать
Пример:
val x = "Hello world" // val x: String
def someFunction() = 42 // def someFunction(): Int
val y = {
if(randomBoolean) {
Cat // наследует Animal
} else {
Dog // наследует Animal
}
}
// val y: Animal
Гибко и удобно использовать систему типов позволяет полиморфизм
Принципиальная возможность для одного и того же кода обрабатывать данные разных типов
Зачастую ставятся определенные условия, которым должны соответствовать типы
В каждом конкретном языке - набор отдельных фич которые дают ту или иную степерь полиморфизма
(subtyping | наследование)
(generics)
(перегрузка функций/операторов, авто приведение типов Int -> Long, в scala - implicit conversion, pattern typeclass)
если для некоторого типа A выполняется какое-то правило
foo(A.value) == OK
то тогда для любого типа B, являющегося подтипом типа A
B <: A
также должно выполняться это правило
foo(B.value) == OK
// для обозначения подтипа в Scala используется "<:"
// Subtype <: Supertype
class Animal
class Cat extends Animal
// Cat <: Animal
class Dog extends Animal
// Dog <: Animal
def foo(x: Animal) = ???
foo(new Cat()) // OK if Cat <: Animal
foo(new Dog()) // OK if Dog <: Animal
foo("Hello world") // compile error
Any - надтип (supertype) всех типов в Scala
Использование:
def foo(x: Any) = ???
val a = if (randomBoolean) {
"Hello world"
} else {
42
}
// val a: Any - Почему..?
AnyVal - тип оберток над значениями
Компилятор с такими типами обходится по особенному - стремится оптимизировать так чтобы обертка не создавала реальный объект в памяти
Использование:Пример создания типа от AnyVal
class Wrapper(val underlying: Int) extends AnyVal {
def foo: Wrapper = new Wrapper(underlying * 19)
}
AnyRef - надтип для всех остальных типов данных, т.е. для всех ссылочных типов.
Использование:
Null - подтип для всех подтипов AnyRef.
Т.е. для любого X <: AnyRef справедливо Null <: X
Это делается автоматически на уровне компилятора
Null
val a = if (randomBoolean) {
"Hello world" // String
} else {
null // Null
}
// val a: String - потому что Null <: String
Nothing
val a = if (randomBoolean) {
42 // Int
} else {
??? // Nothing
}
// val a: Int - потому что Nothing <: Int
Полиморфизм подтипов (subtyping) - имеет пересечения и взаимодействие с другими видами полиморфизма.
Точнее ряд фич системы типов Scala, которые относятся к полиморфизмам другого типа, учитывают правила которые справедливы для подтипов
def someMethod[T](x: T) = ???
class List[A] {
def add(a: A) = ???
}
Нельзя задать переменную с типом List[A]:
val x: List[A] // compile error
Можно задать переменную с типом, сконструированным с помощью дженерика:
val x: List[Int] // OK - тип списка интов
List[A] - задает как бы семейство или множество типов различных типов
Можно на дженерик также смотреть как на конструктор типа
допустим у нас есть некий тип X и мы с помощью такого конструктора List[_] можем сконструировать тип List[X]
Пример: берем тип Int, берем конструктор типа List[_] - конструируем конкретный тип List[Int]
// если Int <: Any то можно ли сказать что List[Int] <: List[Any]
val x: List[Int]
val a: List[Any] = x // OK?
Подобные отношения среди типов одного семейства называются вариантностью
Ковариантный (covariance) - Producer[+A]
Ковариантный (covariance) - Producer[+A]
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
}
class Dog extends Animal {
def makeSound = "bark"
}
def fooWithCovarianceParam(items: List[Animal]): Unit = {
//some side effect
}
def fooWithInvariantParam(items: Array[Animal]): Unit = {
//some side effect
}
fooWithCovarianceParam(List(new Dog())) // OK, так как List[+A] ковариантен
fooWithInvariantParam(Array(new Dog()))// ERROR, так как тип Array[A] инвариантен
Проблема ковариантности
//допустим, MutableList - ковариантен. Помимо этого MutableList - мутабелен
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
def jump = "jump"
}
class Dog extends Animal {
def makeSound = "bark"
}
def addDog(xs: MutableList[Animal]) = {
xs += new Dog()
}
val cats = MutableList[Cat](new Cat(), new Cat())
val horror = addDog(cats)
cats.forEach(cat => cat.jump) //error
Контравариантный (contravariance) - Consumer[-A]
Контравариантный (contravariance)
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
}
class Dog extends Animal {
def makeSound = "bark"
}
class Vet[-A]
def treatDogs(vet: Vet[Dog]) {}
val commonVet = new Vet[Animal]()
treatDogs(commonVet) // OK, ошибки не будет, так как Vet контрвнтный