Вы в Питере живете? Если да, то вас не затруднит показать фото с испорченным видом?
Просто я ежедневно вижу эту башню начиная от Кантемировского моста и далее проезжая Ушаковскую набережную. Дальше ее с набережных просто не видно.
Кантемировский мост — это как раз там, где стоит трехсотметровая телевышка.
При других ракурсах на башню там вид на намывные территории и ЗСД.
Единственное историческое здание, которой можно снять с земли с отчетливо различимой башней — Каменноостровский дворец. И то это будет не его парадный вид, а вид несколько сбоку.
Из центра на данный момент башня не видна с земли. С Исакиевского собора почти наверняка видна, но оттуда и порт с кранами видно. Пока там еще нет шпиля — после его монтажа может что-то и изменится, но изливать такие потоки яда по поводу столь отдаленного от центра здания пока преждевременно.
Если хотите написать о преимуществах scala, то надо с ними очень хорошо ознакомиться. Все-таки побить Dart на его поле (или хотя бы играть с ним на равных) можно только очень хорошо зная scala.
Лично я не настолько хорошо знаком с разработкой FE, чтоб писать такие статьи.
Просто scala.js отлично подходит тем, кто уже залез в глубины scala и не готов расставаться. А вот для изучения… безумству храбрых поем мы песню =).
Преимущества scala.js проявиться могут либо в единой кодовой базе с BE на jvm, либо в хардкорном метапрограммировании, либо в не менее хардкорном FP с вложенными монадами и прочими прелестями. Еще опция — формализация вашей задачи и описание в виде DSL.
Для всего этого надо быть scala спецом, решившим покодить под FE, а не наоборот.
А вот что с нуля вам предложить как киллерфичу scala.js я даже не представляю.
Function0 — функция арности 0 (количество аргументов = 0).
Аналог Supplier в java 8. Сокращенная запись () => T, но можно записать и как Function0[T] (не ясно, правда, зачем). https://scalafiddle.io/sf/qs25LOB/1:
Зачем в java return понятно — там есть множество синтаксических конструкций, не являющихся выражениями. В первую очередь это if и try.
В этом отношении в scala return не нужен.
И тем более он не нужен внутри лямбды: лямбда должна состоять из небольшого количества выражений, в идеале однострочник. Если же требуется что-то большое — пишите метод (и для java 8 работает).
Иногда создавать ADT либо излишне, либо невозможно.
Излишне это бывает, например, в Validation, когда комбинируются ошибки из разных источников. Может оказаться, что каждый второй метод требует специального ADT под тип ошибки, да еще и мапить ошибки постоянно. С объединениями же можно написать так:
Validated[Error1 | Error2, Result].
В Dotty собираются ввести nullable типы следующим образом: T? становится синонимом для T | Null. Это тоже весьма удобно и не влечет потери в производительности.
Но как основную причину появления объединений в Dotty я слышал то, что в некоторых случаях при попытке свернуть тип выражения (например 2 веток if) компилятор упирался в бесконечные или просто невыразимые типы, так что объединения упростили компилятор и дизайн языка.
Тут я все-таки опять не достаточно хороший пример привел. Тип результата не обязан совпадать с типом параметра. Совсем синтетический пример: https://scalafiddle.io/sf/apdsGEh/4.
Также есть zip на HList, возвращающий строго типизированный HList и многие другие методы.
Подобное требуется при метапрограммировании.
22. Немножко громоздко, но можно привыкнуть. Я правильно понимаю, что вот для такого понадобятся дополнительные скобки?
val res = for {
x <- 1 to 15 by 2
z = x*x -1
if z % 3 == 0
y <- 1 to 10 by 3
} yield z -> y
Future же к коллекции не привести, или я не прав? Да и потеря типа не приятна.
Есть аналог async/await?
Там, на самом деле более сложный вопрос — как в for работать с Future[Validation[...]] — для асинхронной обработки результата. В scala для этого monad transformers.
24. Мне кажется больше проблем в десериализации generic типа — если экземпляр создан в java, то у него не будет метаинформации о конкретном типе с которым он создан. И ceylon код, рассчитывающий на эту информацию будет несколько удивлен.
Жаль. Возможность возвращать разный тип результата в зависимости от типа аргумента в HList::map позволяет делать очень интересные вещи.
Например глубокое сравнение для case class:
Раз уж в статье идет сравнение со scala, я позволю себе развить эту тему: 18# Типы — объединения (union types)
В большинстве языков программирования функция может может возвратить значения строго одного типа.
Это если не учитывать наследование и ADT.
Для возвращения ошибок есть Validation
val p = Validated.catchNonFatal {
Integer.parseInt("56")
}
Если же очень хочется именно объединения, то это потребует не намного больше кода:
type F = Boolean :+: Double :+: String :+: Null :+: List[Nothing] :+: CNil
def f(): F = {
val rnd = util.Random.nextInt(5)
rnd match {
case 0 => Coproduct[F](false)
case 1 => Coproduct[F](1.0)
case 2 => Coproduct[F]("2")
case 3 => Coproduct[F](null)
case _ => Coproduct[F](List.empty)
}
}
val v = f()
v.select[Double].foreach { d => println(s"Double $d") }
v.select[Int].foreach { i => println(s"Int $i") } // не скомпилируется, v не может быть Int
Кстати, в ceylon можно ли обработать весь тип объединения полиморфным методом с сохранением типов?
object headOption extends (List ~> Option) {
def apply[T](l: List[T]) = l.headOption
}
val x = Coproduct[List[Int] :+: List[String] :+: CNil]("str" :: Nil)
val res: Option[Int] :+: Option[String] :+: CNil = x map headOption
res.select[Option[Int]].isEmpty
// true
res.select[Option[String]].nonEmpty
// true
19# Типы — пересечения (Intersection types)
Это не проблема тех пор, как придумали наследование:
trait CanRun {
def run() = println("I am running")
}
trait CanSwim {
def swim() = println("I am swimming")
}
trait CanFly {
def fly() = println("I am flying")
}
case class Duck() extends CanRun with CanSwim with CanFly
case class Pigeon() extends CanRun with CanFly {}
case class Chicken() extends CanRun {}
case class Fish() extends CanSwim {}
def f(arg: CanFly with CanSwim) = {
arg.fly();
arg.swim();
}
f(Duck()); //OK Duck can swim and fly
f(Fish()); //ERROR = fish can swim only
20# Типы — перечисления (enumerated types)
Обычно это называется ADT (algebraic data type).
В scala тоже позволяет компилятору проверять полноту сопоставления с образцом.
23# Алиасы типов (Type aliases)
Или на класс, причем класс с конструктором:
Интересный синтаксис, но мне кажется удобнее сделать ссылку на весь компаньон, чтоб получить все методы:
type MyList[T] = List[T]
val MyList = List
val ml1: MyList[Int] = MyList.empty
val ml2: MyList[Int] = MyList("str")
21# Кортежи
Выше уже отметил, что лучшим аналогом в scala является HList.
val res = for {
x <- 1 to 15 by 2
if x % 3 == 0
y <- 1 to 10 by 3
} yield x -> y
// Vector((3,1), (3,4), (3,7), (3,10), (9,1), (9,4), (9,7), (9,10), (15,1), (15,4), (15,7), (15,10))
Это только для коллекций или можно обобщить на произвольную монаду?
24# Улучшенные дженерики
Вообще хорошо, но нельзя забывать, что это влечет некоторые проблемы при взаимодействии с java. Например с java библиотеками для сериализации в JSON.
24# Метамодель
Звучит очень хорошо. Это полноценный механизм макросов как в scala? Можно при помощи него генерировать новые классы во время компиляции?
#25 Общий дизайн языка
Дискуссионный момент. Кому-то (например мне) может больше нравиться подход scala, где сделан упор на расширяемость языка, что позволяет делать такие сторонние библиотеки как shapeless, cats и другие, привносящие в язык новые концепции.
Судя по описанию, кортежи в ceylon ближе к HList, чем к Tuple.
В связи с чем вопрос: можно ли выполнить map по кортежу с сохранением информации о типах элементов?
Примерно так:
Это компромисс для плавного перехода с java. Если не ошибаюсь негативные его последствия были описаны еще в первой версии Programming in Scala.
Нужен он чтоб подобное работало:
for (i <- 0 to (array.length - 1)) {
if (...) return array(i)
}
За пределами добра и зла это было когда в этом исключении был не отключен стектрейс. Теперь это не слишком дорого.
Не думайте, что на эти грабли наступают многие. Чтобы воспользоваться return в лямбде в scala для возвращения результата из этой лямбды надо обладать неординарным воображением.
Просто я ежедневно вижу эту башню начиная от Кантемировского моста и далее проезжая Ушаковскую набережную. Дальше ее с набережных просто не видно.
Кантемировский мост — это как раз там, где стоит трехсотметровая телевышка.
При других ракурсах на башню там вид на намывные территории и ЗСД.
Единственное историческое здание, которой можно снять с земли с отчетливо различимой башней — Каменноостровский дворец. И то это будет не его парадный вид, а вид несколько сбоку.
Из центра на данный момент башня не видна с земли. С Исакиевского собора почти наверняка видна, но оттуда и порт с кранами видно. Пока там еще нет шпиля — после его монтажа может что-то и изменится, но изливать такие потоки яда по поводу столь отдаленного от центра здания пока преждевременно.
Лично я не настолько хорошо знаком с разработкой FE, чтоб писать такие статьи.
Еще там нет поддержки баз данных. И вообще много чего. Но работать вполне можно.
Просто scala.js отлично подходит тем, кто уже залез в глубины scala и не готов расставаться. А вот для изучения… безумству храбрых поем мы песню =).
Преимущества scala.js проявиться могут либо в единой кодовой базе с BE на jvm, либо в хардкорном метапрограммировании, либо в не менее хардкорном FP с вложенными монадами и прочими прелестями. Еще опция — формализация вашей задачи и описание в виде DSL.
Для всего этого надо быть scala спецом, решившим покодить под FE, а не наоборот.
А вот что с нуля вам предложить как киллерфичу scala.js я даже не представляю.
Function0 — функция арности 0 (количество аргументов = 0).
Аналог Supplier в java 8. Сокращенная запись () => T, но можно записать и как Function0[T] (не ясно, правда, зачем). https://scalafiddle.io/sf/qs25LOB/1:
Зачем в java return понятно — там есть множество синтаксических конструкций, не являющихся выражениями. В первую очередь это if и try.
В этом отношении в scala return не нужен.
И тем более он не нужен внутри лямбды: лямбда должна состоять из небольшого количества выражений, в идеале однострочник. Если же требуется что-то большое — пишите метод (и для java 8 работает).
А return для тех, кто документацию не прочитал.
Выше я описал единственную адекватную причину использовать return после прочтения инструкции.
Излишне это бывает, например, в Validation, когда комбинируются ошибки из разных источников. Может оказаться, что каждый второй метод требует специального ADT под тип ошибки, да еще и мапить ошибки постоянно. С объединениями же можно написать так:
Validated[Error1 | Error2, Result].
В Dotty собираются ввести nullable типы следующим образом: T? становится синонимом для T | Null. Это тоже весьма удобно и не влечет потери в производительности.
Но как основную причину появления объединений в Dotty я слышал то, что в некоторых случаях при попытке свернуть тип выражения (например 2 веток if) компилятор упирался в бесконечные или просто невыразимые типы, так что объединения упростили компилятор и дизайн языка.
Тут я все-таки опять не достаточно хороший пример привел. Тип результата не обязан совпадать с типом параметра. Совсем синтетический пример: https://scalafiddle.io/sf/apdsGEh/4.
Также есть zip на HList, возвращающий строго типизированный HList и многие другие методы.
Подобное требуется при метапрограммировании.
22. Немножко громоздко, но можно привыкнуть. Я правильно понимаю, что вот для такого понадобятся дополнительные скобки?
Future же к коллекции не привести, или я не прав? Да и потеря типа не приятна.
Есть аналог async/await?
Там, на самом деле более сложный вопрос — как в for работать с Future[Validation[...]] — для асинхронной обработки результата. В scala для этого monad transformers.
24. Мне кажется больше проблем в десериализации generic типа — если экземпляр создан в java, то у него не будет метаинформации о конкретном типе с которым он создан. И ceylon код, рассчитывающий на эту информацию будет несколько удивлен.
Вот пример полиморфного метода: https://scalafiddle.io/sf/8Sq4LJv/0
Например глубокое сравнение для case class:
18# Типы — объединения (union types)
Это если не учитывать наследование и ADT.
Для возвращения ошибок есть Validation
Если же очень хочется именно объединения, то это потребует не намного больше кода:
Кстати, в ceylon можно ли обработать весь тип объединения полиморфным методом с сохранением типов?
19# Типы — пересечения (Intersection types)
Это не проблема тех пор, как придумали наследование:
20# Типы — перечисления (enumerated types)
Обычно это называется ADT (algebraic data type).
В scala тоже позволяет компилятору проверять полноту сопоставления с образцом.
23# Алиасы типов (Type aliases)
Интересный синтаксис, но мне кажется удобнее сделать ссылку на весь компаньон, чтоб получить все методы:
21# Кортежи
Выше уже отметил, что лучшим аналогом в scala является HList.
22# Конструирование коллекций (for comprehensions)
Многомерное итерирование поддерживатеся?
Это только для коллекций или можно обобщить на произвольную монаду?
24# Улучшенные дженерики
Вообще хорошо, но нельзя забывать, что это влечет некоторые проблемы при взаимодействии с java. Например с java библиотеками для сериализации в JSON.
24# Метамодель
Звучит очень хорошо. Это полноценный механизм макросов как в scala? Можно при помощи него генерировать новые классы во время компиляции?
#25 Общий дизайн языка
Дискуссионный момент. Кому-то (например мне) может больше нравиться подход scala, где сделан упор на расширяемость языка, что позволяет делать такие сторонние библиотеки как shapeless, cats и другие, привносящие в язык новые концепции.
В связи с чем вопрос: можно ли выполнить map по кортежу с сохранением информации о типах элементов?
Примерно так:
Полный код по ссылке.
Нужен он чтоб подобное работало:
За пределами добра и зла это было когда в этом исключении был не отключен стектрейс. Теперь это не слишком дорого.
Не думайте, что на эти грабли наступают многие. Чтобы воспользоваться return в лямбде в scala для возвращения результата из этой лямбды надо обладать неординарным воображением.