Comments 51
DOT — декларативный язык описания графов, dotty — утилита из пакета Graphviz (практически индустриальный стандарт в области визуализации графов) для интерактивного редактирования графов.
Для Either[T,T]
левая и правая ветки отличаются, а для T|T
— нет. Более выпуклый пример: в Option[String] | Option[Int]
один и тот же None
для обоих вариантов, а в Either[Option[String],Option[Int]]
— разные. Так что объединения не будут заменой Either
во всех случаях. То же касается Option
, который можно понимать как прямую сумму чего-то и ничего.
Хотя во многих случаях код станет компактнее и проще, возможность делать нетривиальные ошибки меня пугает.
Боитесь вещей вида Seq[T] | List[T]
? Но и в текущем варианте можно напортачить аналогично, сделав сначала case s: Seq[T] =>
в match
. Я, правда, не помню, что на это скажет компилятор.
val obj = List("asdf", "zxcv")
obj match {
case s: Seq[String] => println(s"seq: ${s}")
case l: List[String] => println(s"list: ${l}")
}
С warning'ом (unreachable code), но работает как ожидается (выводит seq: List(asdf, zxcv)
).
Seq и List просто как пример двух типов, связанных отношением родитель-предок.
В реальности там будут какие-нибудь data object'ы или сообщения с иерархией глубины 3-4 и ветки match'а по 5-10 строк. И приплыли к относительно малозаметному багу.
Да и вообще использовать типы для бизнеслогики — это неправильно.
К счастью, да. Это несколько спасает. Но case class вполне может имплементировать несколько типажей, как вариант, и проблема останется, скорее всего.
case class User(name: String)
class SuperUser extends User("Super User")
println(new SuperUser().name)
Все равно она какая-то странная:
scala> val s = new SuperUser
s: SuperUser = User(Super User)
scala> s match { case User(n) => n }
<console>:16: error: constructor cannot be instantiated to expected type;
found : User
required: SuperUser
s match { case User(n) => n }
На самом деле, не совсем противоречит, если посмотреть с другой точки зрения.
type C = A & B
говорит про тип C в котором есть пересечение двух этих типов (который является A and B).
А дезъюнктивный (в некотором смысле сумма) тип является типом-суммой двух исходных. Как тот же Product (и все его наследники, включая различные TupleN) являются типами-произведениями.
trait A { def f() = 1 }
trait B { def f() = 2 }
type C = A|B
val x:C = ...
x.f()
class C extends A(42) — Это зависимый тип или параметр конструктора?
Что будет, если написать:
def f(x:A(42)) = ...
class X extends A(1)
val x:X = ...
f(x)
Можно ли писать так:
val a: Int = 42
class C extends A(a)
- Нет. Это как раз задача для применения классического полиморфизма. Пересечение типов нужно скорее для того, чтобы например ограничить диапазон принимаемых типов в аргументе функции для классов, которые не имеют общего супертипа.
- 42 в выражении A(42) является параметром конструктора. Запись
является некорректной, так как типом может являться только A. Для параметризации типов используются квадратные скобки. Подробнее тему параметризации типов я раскрыл в разделе про лямбда выражения для типов.def f(x: A(42)) = ...
- Можно, если trait A и константа a определены в одной области видимости, например так:
object Test { val a: Int = 42 trait A(val i: Int) class C extends A(a) }
Если же trait A(i: Int) определен где-то в другом месте — то нет.
trait Container[A] {
def put(a: A): Unit
def count: Int
}
class StringAndIntContainer extends Container[String | Int]
Здесь базовая реализация не предполагает перегрузки метода put, и c помощью объединения типов мы можем создать такой контейнер, который будет принимать на вход String и Int, но компилятор будет запрещать вызывать метод put для других типов.
А как быть с Either в случае, например, List[String | Int | Long | Date | URL | SomethingElse]?
Почему это плохо?
Самое близкое — https://infoscience.epfl.ch/record/222780?ln=en
Жаль только не выйдет подключить систему типов Dotty к JavaScript вместо Flow. В будущем, имхо, будет возможность использовать runtime одного языка, а систему типов другого: c++ с типами haskell или golang с "типизацией" от python.
Со слов Мартина Одерского...
Его зовут Мартин Одерски и фамилия не склоняется же :)
Теперь мы знаем, кому точно лень.
на некоторых платформах вообще только jvm и есть
Lua в этом смысле ещё более распространен (но не luajit). Не особо высокая производительность без jit'а, но работает везде, где есть Си и некоторое количество динамической памяти.
(Хинт: на андроиде си прилепливается скотчем в скромных местах где SDK не нужен, а нужен он в 90% мест.)
Вы о чём спорите? Я лишь опроверг ваше утверждение "на некоторых платформах вообще только jvm и есть", хотя по духу с исходным утверждением согласен, кроме означенного фрагмента.
Где есть jvm почти гарантированно есть плюсы (возможно есть живые имплементации jvm не на плюсах, но я их не встречал), и, уж тем более, си. Разговор не про удобство разработки с использованием конкретного языка/платформы, а про наличие.
Dotty – будущее языка Scala