От автора:


Эта статья представляет собой заметки на коленке и является скорее кратким обзором Kotlin, включая небольшое сравнение с языком С# с точки зрения синтаксиса. Это мое мнение и мои размышления по поводу этого сравнительно молодого языка в мире Java-платформы, который на мой взгляд имеет хорошие шансы добиться успеха.


Kotlin представляет собой статически типизированный объектно-ориентированный язык программирования, компилируемый для платформ Java (еще и JavaScript). Разрабатывается с 2010 года компанией JetBrains. Релиз при этом состоялся не так давно. Авторы ставили целью создать язык более лаконичный и типобезопасный, чем Java, и более простой, чем Scala. Следствием упрощения по сравнению со Scala стали также более быстрая компиляция и лучшая поддержка языка в IDE. Помимо всего прочего, когда компания объявила о разработке данного языка, на нее обрушился шквал критики по поводу того, что лучше бы разработчики довели до ума плагин для Scala (у которой как я понимаю, до сих пор нет нормальной IDE). Однако, для компании язык программирования является достаточно важным инструментом, а разработчики Java не совсем спешат внедрять в язык новую функциональность. И дело даже не в том, что этого не хотят, а из-за того, что слишком много кода написано и слишком много систем работает на этой платформе. И вот приходится тянуть обратную совместимость за собой как балласт. И даже если в последней, 8 версии языка и добавили новые фичи (как лямбда-выражения, например), то мир Enterprise не кинулся обновлять JVM, что заставляет программистов сидеть на той версии, которая стоит у заказчика. Как показывает опыт, некоторые заказные предприятия и фирмы не так давно обновили свои машины только до 7 версии, а заставлять обновлять несколько сотен машин в системе до 8 версии будет слишком не удобно да и дорого для компании заказчика. С моей точки зрения, такая латентность языка в развитии характеризует его как достаточно развитый и мощный инструмент, что может дать представление о том, как часто он используется. Однако, по сравнению с другими языками Java иногда кажется многословной, но это мое мнение как человека, который достаточно программировал на C# и использовал к примеру тот же LINQ, лямбда-выражения и другие плюшки синтаксического сахара, которые делают код компактнее.
Поэтому люди в JetBrains решили сделать язык, который при полной совместимости с Java, предоставит дополнительные возможности, упрощающие повседневную работу программиста и повышающие продуктивность.

Знакомство...


Столкнулся я с ним случайно. Программируя на Java, я скучал по плюшкам из C# и хотелось бы как-то угодить и себе и соответствовать требованиям заказчика. Просмотрев документацию по Kotlin, я понял, что это то, что мне необходимо. Документация в 150 страниц читается достаточно легко, язык прост в изучении и достаточно лаконичен. Однако, мне больше всего понравилось то, что он имеет достаточно общего с C# и работа с языком становится еще приятней. Все-таки забывать .NET не хочется.

Вкусности...


Работа с классами


Ну а теперь перейдем к самому интересному и рассмотрим некоторые особенности языка и что мне в нем нравится.
Как объявить класс в Kotlin:
class Man {
  var name: String //var - для изменяемых переменных, val - для неизменяемых
  var age: Int

  constructor(name: String, age: Int) {
    this.name = name
    this.age = age
  }
}

Почти ничего необычного, за исключением того, что конструктор помечен ключевым словом constructor. На самом деле — это вторичный конструктор с точки зрения Kotlin(а), а первичный или основной конструктор является частью заголовка класса:
class Man constructor(var name: String, var age: Int) 
//или еще можно без ключевого слова
class Man (var name: String, var age: Int)

Точной такой же синтаксис эквивалентен коду, что был описан ранее. Переменные name и age также присутствуют в классе и были соответственно созданы в первичном конструкторе при помощи var (достаточно интересная особенность). С первого взгляда непривычно, но через некоторое время понимаешь, что очень даже удобно. Но основной конструктор не может содержать любой код, поэтому есть блок инициализации (init), который вызывается каждый раз при создании объекта:
class Man (var name: String, var age: Int){
  init {
    //какие-то операции
  }
}

Интересно на мой взгляд. Можно также сделать цепочку конструкторов:
class Man (var name: String){
  var name: String? = null //типы, поддерживающие null, объявляются так и это относится ко всем типам,а не только к значимым, как в C#
  var age: Int = 0 //здесь необходима явная инициализация, так как это свойство, getter и setter использованы по умолчанию
  constructor(name: String, age: Int) : this(name) {
    this.age = age
  }
}

Интересно реализованы здесь свойства и полный синтаксис для объявления:
var <propertyName>: <PropertyType> [= <property_initializer>]
  [<getter>]
  [<setter>]

Инициализатор, getter и setter необязательны, если описывать класс, как было показано в первом примере. Если же переменную описывать как val, то setter соответственно запрещен. Как описывать свойства:
class Man {
  var name: String
    get() {
      return "Name man: $field" //field - представляет собой поле, к которому нужно получить доступ. Если getter определен под объявлением переменной, field соответственно относится к этой переменной
    }
    private set(value) { //изменить переменную вне класса соответственно не получится
      field = value
    }
  var age: Int

  constructor(name: String, age: Int) {
    this.name = name
    this.age = age
  }
}

Data Classes


Представляют интерес Data Classes. Данные классы используются для хранения данных и больше ничего не делают. Компилятор автоматически выводит члены из всех свойств, заявленных в основном конструкторе:
  • equals()/hashCode()
  • метод toString() формы Man(«Alex», 26)
  • функции для соответствующих свойств в порядке их объявления (деструктурированные объявления)
  • функция copy()

Это предоставляет удобство при работе с классами подобного типа:
data class Man (var name: String, var age: Int)

fun main(args: Array<String>) {
var man = Man("Alex", 26) //экземпляр класса создается без оператора new
println(man) //выведет Man(name=Alex, age=26)

//деструктурированные объявления
val (name, age) = man //можно и так: val name = man.component1(); val age = man.component2();
println(name) //выведет Alex 
println(age)  //выведет 26  

//функция copy()
var man2 = man.copy() //просто скопирует объект, не ссылку
var man2 = man.copy(age = 20) //скопирует объект, но с указанными изменениями
println(man2) //Man(name=Alex, age=20)
}

На этом описание классов я бы хотел закончить и перейти к той части языка, которая является его изюминкой.

Functions and Lambdas


Функции в Kotlin объявляются при помощи ключевого слова fun и могут быть определены глобально без привязки к конкретному классу.
fun f1(x: Int): Int {
  return x * 2
}
//или так
fun f1(x: Int): Int = x * 2 //это именованная функция

fun main(args: Array<String>) {
  println(f1(5)) //выведет 10
}

Функции также могут быть вызваны при помощи инфиксной нотации, когда:
  • Они являются функциями-членами или функциями расширения
  • Они имеют один параметр
  • Они отмечены ключевым словом infix

//Определяем расширение для Int
infix fun Int.extent(x: Int): Int {
  return this + x
}

//или так
infix fun Int.extent(x: Int) = this + x

fun main(args: Array<String>) {
  //вызов функции-расширения при помощи infix нотации
  println(5 extent 10) //выведет 15
  //что эквивалентно вызову
  println(5.extent(10))
}

Также функции имеют именованные параметры и значения аргументов по умолчанию.
Можно передавать переменное число аргументов:
fun <T> asList(vararg ts: T): List<T> {
  val result = ArrayList<T>()
  for (t in ts) // ts is an Array
    result.add(t)
  return result
}

fun main(args: Array<String>) {
  val list = asList(1, 2, 3) //вернет список, состоящий из этих чисел
}

Поддерживаются локальные функции (в C# 7.0 также эту функцию реализовали)
fun f1(x: Man): String {

  fun isTeenager(age: Int): Boolean {
    return age in 13..19
  }
  if (isTeenager(x.age))
    return "Man teenager"

  return "Man is not a teenager"
}

Функции высшего порядка и лямбда-выражения


Отдельный интерес представляет собой эта часть языка. Функциями высшего порядка обычно называют функции, которые принимают в качестве аргументов другие функции или возвращают другую функцию в качестве результата. При этом основная идея состоит в том, что функции имеют тот же статус, что другие объекты данных. Использование функций высшего порядка приводит к абстрактным и компактным программам, принимая во внимание сложность производимых ими вычислений.
Рассмотрим пример функции высшего порядка:
//Определяем функцию высшего порядка, аргумент в виде функции, которая возвращает булево значение
fun<T> List<T>.filter(transform: (T) -> Boolean): List<T> {
  val result = arrayListOf<T>()
  for (item in this) {
    if (transform(item)) {
      result.add(item)
    }
  }
  return result
}

fun main(args: Array<String>) {
  val list = arrayListOf(1, 4, 6, 7, 9, 2, 5, 8)
  val listEven = list.filter { item -> item % 2 == 0 }
  listEven.forEach { item -> print(item.toString() + " ") } // вывод: 4 6 2 8
}

Подобный подход позволяет писать код в стиле LINQ:
strings.filter { it.length == 5 }.sortBy { it }.map { it.toUpperCase() }

Полный синтаксический вид лямбда-выражения выглядит следующим образом:
val sum = { x: Int, y: Int -> x + y }

При этом если оставить дополнительные аннотации, это будет выглядеть так:
val sum: (Int, Int) -> Int = { x, y -> x + y }

В круглых скобках всегда указываются параметры, которые затем передаются в тело при помощи ->.
Одна вещь, которая отсутствует в синтаксисе лямбда-выражения, это возможность указать тип возвращаемого значения. В большинстве случаев это излишне, потому что возвращаемый тип может быть выведен автоматически. Однако, если его нужно явно указать, можно использовать альтернативный синтаксис: анонимная функция.
fun(x: Int, y: Int): Int = x + y

//альтернативный вариант
val listEven = list.filter(fun(item) = item % 2 == 0)

Карринг и частичное применение функции


Рассмотрим в качестве примера карринг и частичное применение функции и сравним реализацию на Kotlin и C#.
Некоторые люди иногда путают (да и я некоторое время назад) термины карринг и частичное применение функции и используют их взаимозаменяемо. И карринг и частичное применение это способы преобразования одного вида функции в другой.

Частичное применение функции


Частичное применение берет функцию с N параметрами и значение для одного из этих параметров и возвращает функцию с N-1 параметрами, такую, что, будучи вызванной, она соберет все необходимые значения (первый аргумент, переданный самой функции частичного применения, и остальные N-1 аргументы переданы возвращаемой функции). Таким образом, эти два вызова должны быть эквивалентны методу с тремя параметрами. На C# для этого будут использоваться делегаты. Конечно, они не являются полной заменой функциям высшего порядка, однако для демонстрации более, чем достаточно.
 class Program
    {
        static Int32 SampleFunc(Int32 a, Int32 b, Int32 c)
        {
            return a + b + c;
        }

//перегруженные версии ApplyPartial принимают аргументы и подставляют их в другие позиции в окончательном выполнении    функции
        static Func<T2, T3, TResult> ApplyPartial<T1, T2, T3, TResult>
            (Func<T1, T2, T3, TResult> function, T1 arg1)
        {
            return (b, c) => function(arg1, b, c);
        }

        static Func<T3, TResult> ApplyPartial<T2, T3, TResult>
            (Func<T2, T3, TResult> function, T2 arg2)
        {
            return (c) => function(arg2, c);
        }

        static Func<TResult> ApplyPartial<T3, TResult>
            (Func<T3, TResult> function, T3 arg3)
        {
            return () => function(arg3);
        }

        static void Main(string[] args)
        {
            Func<Int32, Int32, Int32, Int32> function = SampleFunc;

            Func<Int32, Int32, Int32> partial1 = ApplyPartial(function, 1);
            Func<Int32, Int32> partial2 = ApplyPartial(partial1, 2);
            Func<Int32> partial3 = ApplyPartial(partial2, 3);

            var resp = partial3(); // эта строчка вызовет исходную функцию
           
            Console.WriteLine(resp);
            Console.ReadKey();
        }
    }

Обобщения заставляют метод ApplyPatrial выглядеть сложнее, чем он есть на самом деле. Отсутствие типов высшего порядка в C# означает, что необходима реализация метода для каждого делегата, который мы хотим использовать. Для этого, возможно, потребуется семейство Action.
Пример кода на Kotlin:
fun sampleFunc(a: Int, b: Int, c: Int): Int {
  return a + b + c
}

fun f3(a: Int, b: Int): Int {
    return sampleFunc(a, b, 3)
}

fun f2(a: Int): Int {
    return f1(a, 2)
}

fun f1(): Int {
    return f2(1)
}

//альтернативный вариант с использованием лямбда-выражений
val sampleFunc = { a: Int, b: Int, c: Int -> a + b + c }
val f3 = { a: Int, b: Int -> sampleFunc(a, b, 3) }
val f2 = { a: Int -> f3(a, 2) }
val f1 = { -> f2(1) }

fun main(args: Array<String>) {
  println(f1()) //выведет 6
}

В Kotlin, как в C# необходимо создавать отдельную функцию (объект) для получения функции с N-1 аргументами. Подходы у языков одинаковые, только в Kotlin это делать удобнее за счет более компактного синтаксиса.

Карринг


В то время как частичное применение преобразует функцию с N параметрами в функцию с N-1 параметрами, применяя один аргумент, карринг декомпозирует функцию на функции от одного аргумента. Мы не передаем никаких дополнительных аргументов в метод Curry, кроме преобразуемой функции:
  • Curry(f) возвращает функцию f1, такую что…
  • f1(a) возвращает функцию f2, такую что…
  • f2(b) возвращает функцию f3, такую что…
  • f3(с) вызывает f(a, b, c)

Реализация на C# будет выглядеть так:
 class Program
    {
        static Int32 SampleFunc(Int32 a, Int32 b, Int32 c)
        {
            return a + b + c;
        }

        static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>
            (Func<T1, T2, T3, TResult> function)
        {
            return a => b => c => function(a, b, c);
        }

        static void Main(string[] args)
        {
            Func<Int32, Int32, Int32, Int32> function = SampleFunc;

            // вызов через карринг
            Func<Int32, Func<Int32, Func<Int32, Int32>>> f1 = Curry(function);
            Func<Int32, Func<Int32, Int32>> f2 = f1(1);
            Func<Int32, Int32> f3 = f2(2);
            Int32 result = f3(3);

            // или соберем все вызовы вместе...
            var curried = Curry(function);
            result = curried(1)(2)(3);

            Console.WriteLine(result); //выведет 6
            Console.ReadKey();
        }
    }

Код на Kotlin:
fun curry(body: (a: Int, b: Int, c: Int) -> Int): (Int) -> (Int) -> (Int) -> Int {
  return fun(a: Int): (Int) -> (Int) -> Int {
    return fun(b: Int): (Int) -> Int {
      return fun(c: Int): Int = body(a, b, c)
    }
  }
}
//без дополнительных аннотаций
fun curry(body: (a: Int, b: Int, c: Int) -> Int) =
        fun(a: Int) = fun(b: Int) = fun(c: Int) = body(a, b, c)

fun main(args: Array<String>) {
  val f = curry { a: Int, b: Int, c: Int -> a + b + c }
  val response = f(1)(1)(1)
  println(response)
}

Inline function


Использование высших функций приводит к накладным расходам. Выделение памяти, на объекты функций, а также последующая очистка. Во многих случаях такого рода издержки могут быть устранены путем подстановки лямбда-выражений. Рассмотрим функцию, которая принимает в качестве параметров функцию, принимает объект блокировки и функции, получает блокировку, выполняет функции и снимает блокировку:
fun <T> lock(lock: Lock, body: () -> T): T {
  lock.lock()
  try {
    return body()
  }
  finally {
    lock.unlock()
  }
}

Однако при вызове происходит создание объекта. Вместо создания объекта, компилятор может вставить следующий код:
l.lock()
try {
  foo()
}
finally {
  l.unlock()
}

Чтобы заставить компилятор это сделать, необходимо добавить в объявлении метода модификатор inline:
inline fun lock<T>(lock: Lock, body: () -> T): T {
  // ...
}

Однако не стоит встраивать большие функции, это может сказаться на производительности. Если есть необходимость в том, чтобы происходило встраивание не всех функций, можно добавить модификатор noinline:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
  // ...
}


Вывод...


Kotlin достаточно интересный язык, который изучать одно удовольствие. Мне нравится его компактный синтаксис и те широкие возможности, которые он предоставляет. Отдельной заслугой стоит упомянуть тот факт, что его можно использовать вместе с Java в одном проекте, что тоже достаточно интересно и дает большую гибкость при создании проекта. Этот язык позволяет быстро разработать программу и причем сделать это довольно красиво. Схожий синтаксис с тем же С# делает его в освоении еще проще, ну и приятнее. Поэтому если кому-то вдруг захочется перейти на платформу Java с платформы .NET, этот язык, возможно, оставит приятные впечатления.

P.S. интересно мнение по поводу этого языка как Java-программистов, так и C#. Стали бы Вы использовать Kotlin в своих проектах?

Комментарии (97)


  1. Bringoff
    17.04.2016 17:01
    +3

    интересно мнение по поводу этого языка как Java-программистов, так и C#. Стали бы Вы использовать Kotlin в своих проектах?

    Почему бы и нет? Я android-разработчик, и особенно в свете недоступности полноценной java 8 для разработки под android, kotlin может быть как глоток воды в пустыне. Другое дело, что времени особо нету играться, а сразу в боевой проект тащить страшно.
    Во всяком случае, в свете слухов о возможном использовании swift на андроиде, кажется мне, что лучше бы это был котлин.


    1. Priest512
      17.04.2016 17:03

      Насколько я знаю, есть плагин, который портирует код с Java на Kotlin.


      1. kvascola
        17.04.2016 22:28

        Kotlin-плагин под Intellij IDEA (и под Android Studio, соответственно) и содержит эту функциональность.


    1. Nagg
      18.04.2016 23:24

      Java8 же обещали быть с Android N. Свифт, думаю, не угрожает яве, т.к. одно дело запустить «хеллоу ворлд» на нем на андроиде, другое — забайндить всё Андроид СДК.


      1. Bringoff
        19.04.2016 10:01
        +1

        Java8 же обещали быть с Android N

        Только работать все будет только на том же N. Бекпорт только пока для лямбд вроде обещали, а их и сейчас можно при желании ретролямбдой получить.


  1. 23derevo
    17.04.2016 17:27
    +4

    Вы бы статью переименовали что ли. А то название как-то мало соответствует содержанию.


    1. Priest512
      17.04.2016 17:28

      Как бы посоветовали?


      1. 23derevo
        17.04.2016 17:35

        Например, написать, что это сравнение языков Kotlin и C#?


  1. Sirikid
    17.04.2016 17:47

    Пишу на Kotlin под Android, про анонимные функции не знал, хотя гайд читал, спасибо.
    Каррирование, кстати, можно реализовать по разному. Вот мой способ, который не требует дополнительных явных вызовов github.com/MarioAriasC/funKTionale/issues/5


  1. Nanako
    17.04.2016 18:29
    +2

    У меня всегда двоякое впечатление от описания всяческого «удобного» синтаксического сахара. Мой C# после всех оптимизаций и фиксов вырос в безумный набор unsafe/fixed/CER/interlocked/methodimpl вуду, аж goto в двух местах есть. И мне сознательно пришлось отказаться от linq, foreach и т.д. почти везде. И когда я читаю производительный код на Java там та же картина. Вобщем без бенчмарков сравнение не катит. А ключевым моментом при выборе .NET/Java для меня был рантайм, AppDomain и все такое. У меня можно почти все и на VB портировать, сомневаюсь что что-то изменится.


    1. Priest512
      17.04.2016 18:38
      +1

      Это сравнение чисто с точки зрения синтаксиса. Производительность тут не затрагивалась. Мне было интересно рассматривать этот язык, потому что он похож на С#. А как заметил мой коллега по работе, производительный код всегда тяжело читать. И как я вижу, иногда жертвуют производительностью ради хорошей читаемости и сопровождения кода. Я думаю, что грамотное использование синтаксического сахара позволит сделать код более приятным. Тот же С# в последних версиях имеет сахар, которым занимается компилятор, а сама платформа не изменяется.


      1. Nanako
        17.04.2016 18:49
        +1

        Ну обеспечение атомарности операций компилятор на себя не возьмет. Что, например, вот отсюда можно сахаром заменить:

                [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
                [MethodImpl(MethodImplOptions.NoInlining)]
                [SecurityCritical]
                public unsafe bool OpenMemoryMap(long memoryMapCapacity, out bool capacityIsSufficient)
                {
                    bool lockTaken = false;
        
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try
                    {
        	        this.CheckDisposed();
        
        	        if (memoryMapCapacity <= 0)
        	        {
        	            throw new ArgumentOutOfRangeException("memoryMapCapacity", StringResources.MemoryMapCapacityOutOfRange);
        	        }
        
                    	bool result = false;
        
                        Monitor.TryEnter(this.stateSyncRoot, ObjectStateTimeout, ref lockTaken);
        
                        if (!lockTaken)
                        {
                        	// some code
                        }
                        else
                        {
                        	// some other code
                        }
        
                        return result;
                    }
                    catch (Exception exception)
                    {
                        Logger.Error(Helper.GetString(StringResources.UnableToAcquireViewHandlePointerBecauseException, this.name, exception));
                        return false;
                    }
                    finally
                    {
                        if (lockTaken)
                        {
                            Monitor.Exit(this.stateSyncRoot);
                        }
                    }
                }
        


        P.S. Выход по таймауту в оригинале перехватывается отдельно, я укоротил немного.


        1. Priest512
          17.04.2016 19:42
          +2

          Мм, интересно, что это у вас за проект такой)) Тут действительно, места ему нет, да и это может сказаться на работе кода. Однако, как я понимаю, это специфичный код. В большинстве случаев от сахара будет польза. К примеру работа с БД. Я как-то работал с MongoDB и там все обращения с выборкой и фильтрацией сделаны через LINQ, что очень удобно и быстро на самом деле. Если я захочу использовать, к примеру, SIMD-расширения в C#, то там код усложнится сразу, зато выше производительность. Или же еще взять то, что в Java в основном рекомендуется работать с интерфейсами, а не с конкретной реализацией, хотя такой подход дает нагрузку на JVM. Однако, на это особо не обращают внимания, так как такой подход позволяет писать удобный код.


          1. Nanako
            17.04.2016 20:02

            Ну у меня в рабочем проекте MongoDB ГОРАЗДО медленнее PostgreSQL на записи json в базу. Конечно мы в PostgreSQL пишем бинарные данные и пакетами, но .NET MongoDB это просто шлак со всем своим LINQ. Т.е. в сотни раз медленнее, на потоке в 4 Гигабита json c 60000+ записей в секунду. Так что не знаю о чем вы.


            1. Priest512
              17.04.2016 20:08
              +3

              Ну опять же, у Вас как я понимаю очень нагруженная система, соответственно специфические требования) В большинстве программ это удобно. Я вообще плюсы использую, когда мне нужна скорость, как у себя в проекте с математическими расчетами. Плюсы и быстрее и удобнее, хоть и требуют очень осторожного подхода) мелкие иногда ошибки, а память кушать начинает огромными темпами


              1. Nanako
                17.04.2016 20:14
                +3

                Ну, давайте так. .NET у нас для надежности, как бы странно это ни звучало. Запуск плагинов в отдельных процессах и AppDomain, контроль лайфтайма объектов и т.д. Очень много бонусов по сравнению с нативным кодом по скорости разработки. Система нагружена из-за реалтаймового обновления всех этих данных, поэтому математическиме расчеты и рендер интерфейса на GPU, куча native кода и т.д. Почему я для ненагруженной системы должен использовать Mongo если Postgres лучше, я не знаю. Память мы частично вручную контролируем, т.к. при таких объемах данных в много потоков, процессов и приложений GC не справляется, но отказавшись от сахара мы снизили потребность в памяти во много раз. Структуры, массивы на стеке, прямой доступ к памяти, Dispose всего и вся очень быстро исправляют ситуацию. Прототип, единичные вызовы могут и должны использовать Linq и все такое, но в 90% случаев от этого приходится отказаться.


                1. Priest512
                  17.04.2016 20:55
                  -5

                  Тут с Вами согласен на все 100%. Тот же LINQ создает отдельно рабочий поток (без вашего ведома) и из последовательности делает выборку по определенному критерию (поэтому рекомендуется большие списки инициализировать в левой части). При этом имеет ленивую инициализацию, так что только дефолтные настройки уже не радуют. Затем тот же async/await, который компилятор за кулисами раскручивает до конечного автомата и создает дополнительные объекты для сохранения состояния метода и всех его переменных (а если у вас много локальных переменных в методе, это опять же память), а затем кидает выполнение асинхронного метода в фоновый поток (благо из пула берется), а потом ожидание выполнения с уведомлением через потоки ввода-вывода системы, число которых равно числу физических ядер процессора (Hyper-Thearing с его виртуальными ядрами не считается) — все это несомненно нагрузка на систему и приводит к постоянным аллокациям и нагрузкам на систему. А учитывая как .NET резервирует память, при помощи полигонов… То, что вы сейчас описали, несомненно нуждается в простых, но надежных методах без всякой «красивой» приблуды. Тут ему естественно делать нечего, поэтому я считаю, что необходимо знать чего стоит эта красота на самом деле. Поэтому часто сижу и читаю код, который генерит компилятор)))


                  1. Priest512
                    17.04.2016 22:30
                    +1

                    Интересно, те кто минусуют, объясните, где я прокололся)


                    1. lair
                      17.04.2016 22:53
                      +7

                      объясните, где я прокололся

                      Например, там, где сказали, что LINQ создает отдельный рабочий поток. Или там, где сказали, что async/await кидает выполнение метода в фоновый поток.


                      1. Priest512
                        17.04.2016 22:57
                        -2

                        А разве LINQ не создает отдельный поток при работе? A async/await передает код на выполнение в поток, который берется из пула потоков?


                        1. lair
                          17.04.2016 22:58
                          +6

                          А разве LINQ не создает отдельный поток при работе?

                          Ни одна виденная мной реализация так не делала. Зачем?

                          A async/await передает код на выполнение в поток, который берется из пула потоков?

                          async-await вообще не управляет тем, где будет выполняться код.


                          1. Priest512
                            17.04.2016 23:02
                            -3

                            Сейчас нет у меня под рукой той книги, в которой читал про создание отдельного потока) Возможно, что-то путаю, я уточню. Насчет async/await да, это всего лишь синтаксис, самое интересное делает компилятор и код, который он генерит, запускает задачу в потоке и ждет Task или Task<_Result_>, которые выполняются в пуле потоков)


                            1. lair
                              17.04.2016 23:10
                              +4

                              нтересное делает компилятор и код, который он генерит, запускает задачу в потоке и ждет

                              А вот и нет. Код, который он генерит, всего лишь создает continuation (в том случае, когда задача еще не выполнилась, иначе просто идет выполнение дальше).


                              1. Priest512
                                17.04.2016 23:32

                                Ну то есть как, происходит проверка на выполнение задачи. Если она не выполнилась, то происходит процедура подписки о завершении задачи Task. Также в процедуре подписки участвует объект AwaitUnsafeOnCompleted, который реализует дополнительные возможности await, в том числе запоминание контекста синхронизации, который нужно будет восстановить при возобновлении. Этот метод планирует конечный автомат для перехода к следующему действию.И вот он как раз занимается задачами… А это подхватывает уже другой поток


                                1. lair
                                  17.04.2016 23:36
                                  +2

                                  А это подхватывает уже другой поток

                                  С чего вы это взяли? «Подхватывает» continuation тот поток, который выбирается шедулером.

                                  (а запоминание контекста синхронизации — это не «дополнительные возможности await», банальный ContinueWith это умеет)


                                  1. Priest512
                                    17.04.2016 23:39

                                    Ну, а планировщик разве не берет поток из пула потоков?


                                    1. lair
                                      17.04.2016 23:39
                                      +2

                                      Совершенно не обязательно.


                                      1. Priest512
                                        17.04.2016 23:41

                                        То есть как? Насколько я помню, Task всегда выполняется в подобного рода потоках? Или же зависит от того, сохранят контекст или нет?


                                        1. lair
                                          17.04.2016 23:44
                                          +3

                                          Для начала — зависит от природы таска, если это IOCP, то поток вообще может быть один и тот же, в котором выполнение и шло (а может и не быть, здесь тоже как шедулеру виднее). А если таск вычислительный, и порожден явно, то просто зависит от шедулера.

                                          (а вот от сохранения контекста зависит только то, где будет выполняться continuation).


                                          1. Priest512
                                            17.04.2016 23:48
                                            +2

                                            Хмм, а во про природу тасков не знал. У Алекса Дэвиса этого не видел, когда читал его книгу про асинхронное программирование)) спасибо


                                            1. ZOXEXIVO
                                              18.04.2016 01:08
                                              -7

                                              Даю совет, не надо учить 100500 языков сразу, выучи НОРМАЛЬНО хотя бы один!


                      1. Nanako
                        17.04.2016 23:23
                        -6

                        Вот и гуру подтянулся.


                1. Iqorek
                  17.04.2016 23:25

                  > Память мы частично вручную контролируем,
                  очень странно то что вы говорите, я когда то делал тесты, небольшие объекты с коротким временем жизни работали процентов на 10% быстрей «ручного управления памятью», новосозданный объект создается в поколении 0, которое всегда находится в кеше процессора и работает с максимально возможной скоростью, объекты с коротким временем жизни очень быстро удаляются из памяти, GC пробегает только по «живым» объектам. Да важно что бы объекты не «бегали» между нативным кодом и управляемым, это уложняет работу GC и вообще много ресурсов тратится на таких переходах.


                  1. Nanako
                    17.04.2016 23:39
                    +2

                    Приведу статистику live системы: 4 гигабита идет в shared memory между процессами, например, каждые 20 мс происходит обновление данных. Во время обновления на стеке создаются 5-6 двумерных массивов на 1000 элементов среди которых в основном структуры, но есть и инстансы классов, массивы нужны чтобы работать с отдельным снэпшотом данных в каждом из нескольких потоков обработки. Сами данные копируются из shared memory, немного конвертируются. Мержит снапшоты отдельный тред. Параллельно при вызове рендера из композишен таргет, в еще одном треде, последний снапшот копируется, обрабатывается и отправляется в видеопамять на рендер, чтобы на следующем вызове рендере обновить из текстуры интерфейс. Сам рендер естественно еще в двух тредах используя deferred context. И еще один тред готовит снапшоты для рендера, инкрементально накатывая диффы, чтобы не гонять снапшоты целиком. Чтобы все это работало без тормозов GC переводится в sustainedlowlatency чтобы не мешал, и приходится все кроме структур диспозить руками, т.к. там дочерта unmanaged ресурсов, присваивать указателям null насильно, т.к. для скорости часть переменных по возможности вынесены непосредственно из методов. Ну и все это по максимуму lock-free. Вот тут и пригождается все вплоть до unchecked, и большая часть данных гоняется в unmanaged через memmove и array.copy.


            1. ZOXEXIVO
              18.04.2016 12:35

              MongoDB создали не для того чтобы он быстрее PostgreSQL писал данные, а для того чтобы эти данные равномерно разливать по постоянно добавляющимся серверам, тем самым нивелируя все объемы и тут ему равных нет. В случае с PostgreSQL и их шардированием все это будет неповоротливо и жутко неудобно.
              Можете описать что делает ваш проект и почему он должен держать такой объем поступающих данных?
              Специфики не знаю, может ошибаюсь, но когда все идут по пути упрощения всего и вся вы создаете комбайн с привязками к платформе, с которым потом никто не сможет ничего сделать. Экономите на серверах?


              1. Nanako
                18.04.2016 13:00

                Ну с такой разницей в производительности меня Citus полностью устраивает для шардинга PostgreSQL. Плюс мне вообще не надо high-accessibility, мне надо fast-writes. Я не могу сейчас привести сравнение производительности распределенных MongoDB и PostgreSQL т.к. не делал этого еще, но чувствую первая от второго недалеко оторвется. У нас специфический софт, торговая платформа, мы в угоду производительности и латентности жертвуем объемами памяти, иногда даже отключаем hyper-threading (да да). Ну и все решения принимались после недель профайлинга на разных конфигурациях и т.д.


          1. Nanako
            17.04.2016 20:05

            И вы не поверите, в продакшен проектах 90% кода так написано, кроме некоторых тестов. Откройте исходники .NET, узнаете для себя много нового. Я неделю копался наверное после релиза.


            1. Priest512
              17.04.2016 20:09

              Читал исходники SIMD-реализации в .NET, достаточно познавательно на самом деле.


      1. Nanako
        17.04.2016 19:11

        А вот производительный код, который да, переоптимизирован немного, для сравнения:

                                if (*((bool*)(internalDataStorage + 3792)))
                                {
                                    Interlocked.Add(ref totalStringLength, -*((int*)(internalDataStorage + 3796)));
                                }
        #if TRACE
                                Logger.Trace("Report::set_ReceivingServerIdField()");
        #endif
                                ((IComponentInternal)this).SetDirty();
        
                                Thread.MemoryBarrier();
                                allOk = true;
        

        Это С#, ага.


        1. Bringoff
          17.04.2016 19:46
          +7

          Не извращайнесь, пишите на С++ :)


          1. Nanako
            17.04.2016 20:03

            Да уже готовлюсь морально к портированию…


  1. Pavelise
    17.04.2016 18:31
    -2

    Отлично, в следующей статье сравните с JS или Python для порядка.


  1. babylon
    17.04.2016 20:00
    -25

    fun asList(vararg ts: T): List — такой синтаксис это яркий пример тупиковости и дегенератства как C# так Котлин.
    И никого это не чешет. Мне лично очевидно что асинхронный код должен исполняться по запросам, данные передаваться как массивы или объекты и также возвращаться. А это бред!!! Не могу на это смотреть… аааа.ааа!


    1. Bringoff
      18.04.2016 06:57
      +5

      А есть что-то по сути сказать, аргументированно?


      1. lair
        18.04.2016 11:04
        +4

        Не, даже и не ждите. По прошлым комментариям хорошо видно.


      1. tundrawolf_kiba
        18.04.2016 13:52
        +1

        Показалось, что его ник знаком и да, действительно видел его уже в статье про БЭМ, где они поливал БЭМ помоями, но при этом не мог привести конкретных аргументов, а то, что ему по этой причине ожидаемо ставили минусы — называл заговором Яндекса(ну или как-то похоже)


  1. Athari
    17.04.2016 20:26
    +3

    Функции также могут быть вызваны при помощи инфиксной нотации

    В стандартной библиотеке инфиксные функции есть? Примеры? А то прикольно, но не очень понятно, зачем. :)


    С каррированием такая же петрушка: всё время говорят, как это круто, а в примерах неизменно "SampleFunction" с "a + b + c"...


    Подходы у языков одинаковые, только в Kotlin это делать удобнее за счет более компактного синтаксиса.

    Только в шарпе дженерики, а в котлине инты… Можете эквивалентный код показать?


    Чтобы заставить компилятор это сделать, необходимо добавить в объявлении метода модификатор inline

    Компилятор сам не догадается? Старнно видеть подобные подсказки компилятору в современном высокоуровневом языке аж на уровне языка.


    1. Sirikid
      17.04.2016 20:54

      Промахнулся и ответил в пост


    1. konsoletyper
      18.04.2016 11:27
      +4

      > Компилятор сам не догадается? Старнно видеть подобные подсказки компилятору в современном высокоуровневом языке аж на уровне языка.

      Обычно, инлайнинг реализуют не на уровне компилятора, а на уровне бэкэнда (HotSpot, LLVM, Crankshaft), которые оперируют более простыми сущностями (SSA, PDG). В Kotlin инлайнинг — это не особенность рантайма, это семантическая особенность. Например, т.к. inline-лямбда гарантированно инлайнится, ей становится доступен поток управления функции, откуда её вызывают. Это нужно вот для чего. Есть у нас языковая контрукция:

      for (i in 1..100) {
          // do something
      }
      


      Теперь мы хотим сделать свою ФВП each и вызывать её так:

      (1..100).each { i ->
          // do something
      }
      


      Проблема в том, что конструкции в случае с Java/JavaScript/C# являются неравнозначными, ибо в первом случае можно делать break/continue/return, а во втором — нельзя. Так вот в Kotlin можно делать как минимум return, т.е. return не из самой лямбды, а из объемлющей именованной функции (а в будущем, надеюсь, добавят поддержку break/continue). Собственно, inline это и означает — встроить код в поток управления вызывающей функции.

      Кстати, на практике VM умеют хорошо инлайнить только мономорфные вызовы, а лямбду переданную, пусть даже в мономорфную ФПВ, заинлайнить уже достаточно проблематично. Так что пока без подсказок компилятору тут никак.


      1. Athari
        18.04.2016 11:54
        +1

        Хм. Прелюбопытно. Этакий мини-макрос получается. Действительно, доступа к управляющим инструкциям из лямбд в подобных случаях, бывает, не хватает.


        Но, по-моему, это усложняет понимание кода: непонятно, куда выкинет return — приходится знать, инлайновый вызов или нет.


        1. Sirikid
          18.04.2016 14:08

          Имхо все достаточно прозрачно описано на сайте языка
          http://kotlinlang.org/docs/reference/inline-functions.html
          http://kotlinlang.org/docs/reference/returns.html


          1. Athari
            18.04.2016 14:19

            Ещё и метки замысловатые… return@forEach — это весело. :)


            Теперь принцип работы понятен, но всё-таки хотелось бы узнать о практическом применении: возникают ли проблемы с тем, что приходится запоминать, где лямбды инлайновые, а где нет, например.


            1. Sirikid
              18.04.2016 14:44

              Я пишу в IDEA, она сразу подсказывает где просто return нельзя, где какие метки можно подставить, но сама необходимость их использования возникает редко.


      1. sergeykh
        18.04.2016 15:06

        Касательно замечания по break/continue/return. Continue можно реализовать с помощью фильтрации набора, еще до самой итерации. Break можно реализовать по средствам return.


    1. konsoletyper
      18.04.2016 11:33

      В стандартной библиотеке инфиксные функции есть? Примеры? А то прикольно, но не очень понятно, зачем. :)

      Битовые операции в Котлине реализованы в виде инфиксных функций or/and/xor

      С каррированием такая же петрушка: всё время говорят, как это круто, а в примерах неизменно «SampleFunction» с «a + b + c»…

      Например, функцию sum в функциональных языках часто реализуют через карринг функции fold. Т.е.

      sum = foldl 0 (+)
      


      Правда, это не очень относится к Kotlin, т.к. это не чисто функциональный язык.


      1. Athari
        18.04.2016 11:48

        Битовые операции в Котлине реализованы в виде инфиксных функций or/and/xor

        Как приоритеты разруливаются?


        1. konsoletyper
          18.04.2016 11:51

          См. доку

          К сожалению (а может, и к счастью), тут нельзя, как в некоторых языках, явно задать приоритет инфиксной функции.


          1. Athari
            18.04.2016 11:59

            Ха-ха, инфиксные функции в приоритетах находятся аккуратно на месте битовых операторов в C++. :)) То есть смешать and и or без скобок нормально не получится (впрочем, многие ставят скобки в любом случае), но относительно других операций приоритет привычный. Приоритет будто специально подобрали для битовых операций. :)


      1. dougrinch
        18.04.2016 15:36
        +1

        Битовые операции в Котлине реализованы в виде инфиксных функций or/and/xor

        Как же я от этого страдал когда нужно было понавертеть много битовых вычислений.


        1. Sirikid
          18.04.2016 16:28

          А я просто написал их на Java, плюс не надо было постоянно toByte() вставлять


  1. Sirikid
    17.04.2016 20:49
    +1

    > В стандартной библиотеке инфиксные функции есть? Примеры? А то прикольно, но не очень понятно, зачем. :)
    Пример 1

    > Только в шарпе дженерики, а в котлине инты… Можете эквивалентный код показать?
    Пример 2, немного другая реализация

    > Компилятор сам не догадается? Старнно видеть подобные подсказки компилятору в современном высокоуровневом языке аж на уровне языка.
    inline-функции инлайнятся всегда и во время компиляции, остальные инлайнятся во время выполнения и только если JIT решит что надо. Плюс у inline-функций generic-параметры могут быть reified.

    gist с примерами https://gist.github.com/sirikid/af1206e798879e02e4949ea22bfc7ba9


  1. adelier
    17.04.2016 22:53
    +4

    Код в самом деле похож на Scala. По поводу IDE из введения «у которой как я понимаю, до сих пор нет нормальной IDE» — в IDEA замечательный плагин для скалы. Эклипсом я не пользуюсь, но там тоже есть поддержка скалы, и её вполне себе используют.


    1. Priest512
      17.04.2016 22:55

      Ясно, просто из истории языка Kotlin читал, что при его создании, многие ругались на компанию, за то что они никак плагин под Scala не допилят)


  1. aminought
    18.04.2016 00:04
    +1

    >Подобный подход позволяет писать код в стиле LINQ
    Вот вы его часто упоминаете. Не знакомы со Streams API из Java 8?


    1. Priest512
      18.04.2016 00:05

      на Java 8 не работал в силу того, что у всех клиентов 7 версия стоит)) однако, киньте ссылочку, полезно будет почитать.


      1. terryP
        18.04.2016 00:49
        +2

        Советую прочитать эту статью, кстати при желании можно и на семерке стримы использовать с помощью retrolamba или других библиотек


  1. arturdumchev
    18.04.2016 01:31
    -3

    Из частичного применения функций.
    Зачем писать 4 функции, когда можно написать одну?

    fun f(i: Int = 1, j: Int = 2, k: Int = 3) = i + j + k
    

    вместо
    fun sampleFunc(a: Int, b: Int, c: Int): Int {
      return a + b + c
    }
    
    fun f3(a: Int, b: Int): Int {
        return sampleFunc(a, b, 3)
    }
    
    fun f2(a: Int): Int {
        return f1(a, 2)
    }
    
    fun f1(): Int {
        return f2(1)
    }
    


    Результат:
    fun main(args: Array<String>) {
        println(f())        //6
        println(f(2))       //7
        println(f(2, 2))    //7
    }
    


    1. Sirikid
      18.04.2016 01:56
      +3

      А теперь напишите для общего случая, с N параметрами заранее неизвестных типов


  1. vba
    18.04.2016 10:29

    Спасибо за статью, я так понимаю это частичное пересечение (intersection) фитч двух языков, было бы неплохо глянуть на взаимо-исключающие различия.


    1. Priest512
      18.04.2016 10:55

      Ну да, целью было показать то, чем языки похожи. Я уже когда читал документацию, заметил много общего с C#. Насчет различий, надо бы понять, какие они))


  1. konsoletyper
    18.04.2016 12:22

    То ли я что-то в этой жизни не понимаю, то ли data class в Kotlin совершенно неверно позиционируют. А именно, повсюду повторяют примерно такую мантру:

    Представляют интерес Data Classes. Данные классы используются для хранения данных и больше ничего не делают.


    На самом деле, для этого не надо никаких data class. Например, вот такой класс:

    class A(var u: Int, var v: String, var w: Boolean)
    


    На самом деле, есть такое понятие как value object (лучше почитать DDD Эванса), и вот, мне кажется, что data class — это отличный способ реализовать данный паттерн. На каждом углу кричат, что value object должны быть immutable, и я с этим мнением вполне согласен (не хочу приводить целую пачку аргументов здесь, кому надо — гуглите по ключевым словам value object immutable). Так вот, отсюда вытекает, что

    1. Если уж не запрещать var в декларации data class, то хотя бы надо выдавать предупреждение, включённое в настройках intentions по-умолчанию
    2. В документации к языку и официальных блог-постах тщательно объяснять этот момент.
    3. Отписываться в комментах к статьям вроде этой, которые, о ужас, предлагают объявлять entity как data class.

    Я уже поднимал эту тему, но мне, к сожалению, сказали, что мой подход чрезмерно религиозен. Ну по крайней мере, буду сам стараться и просвещать по мере сил.


    1. Priest512
      18.04.2016 13:43

      Мм, интересно Ваше мнение. А где можно поподробнее почитать


    1. Priest512
      18.04.2016 13:43

      Мм, интересно Ваше мнение. А где можно поподробнее почитать?


      1. konsoletyper
        18.04.2016 14:59
        +1

        Ну в философском смысле можно развести долгую дискуссию насчёт state/identity, о том, что у иммутабельных объектов их нет, и поэтому сравнение можно производить только по совокупности полей, хотя иммутабельные объекты — это никакие не объекты, а хак в ООП для эмуляции value. Проще говоря, есть примитивы вроде int/long — есть ли у них состояние? А что если мы хотим ввести свои «примитивы» вроде Vector, ComplexNumber, Address? Кстати, один такой «примитив» есть в JDK — String. Есть же объекты, которые меняют состояние, поэтому сравнивать их по состоянию некорректно, т.к. они могут иметь одинаковое состояние в какой-то момент времени, но это не означает, что это один и тот же объект. Пример — близнецы: у них очень многие параметры похожи, их даже (теоретически) могут назвать одинаково, но это всё-таки два разных человека, т.к. у них есть identity.

        С практической точки зрения переопределение equals/hashCode, которые нестабильны (т.е. изменяются со временем жизни объекта) может привести к поломке HashSet/HashMap. Да и контракт hashCode явно требует его стабильности. Зависимость hashCode от состояния объекта (т.е. от поля, объявленного как var) как раз делает hashCode нестабильным.


  1. KIVan
    18.04.2016 15:00
    +1

    «Стали бы Вы использовать Kotlin в своих проектах?»

    Да, стал бы. По опыту, я вижу большие перспективы у подхода «одна VM — много языков» или даже Язык++ с удобным сахаром транспилируемым в просто Язык. Самым ярким примером для меня стал TypeScript, который позволял использовать плюшки ES2015 еще в 2013-ом году на продакшене, не говоря уже о его собственных плюшках. Потом появился Roslyn, сделав C# раширяемым (хотя тут главное не переборщить). Для банковской сферы, где новый код должен быть совместим со старым без вариантов, это пока единственный жизнеспособный подход что я видел чтоб не застревать на старых версиях языков годами.


  1. mrigi
    18.04.2016 15:05

    Совершенно не понятно зачем на VM тащить мусор вроде inline. Это сугубо работа компилятора и нечего лезть к нему со своими указами. Если нужно выжимать максимум, для этого есть куда более подходящие языки программирования.

    Почему-то мне всегда не нравился println и ему подобные. Во первых, почему print когда на самом деле никакой связи с печатью там нет. Во вторых, зачем там экономия жалких двух символов. А если и нужна экономия, то почему тогда слово print без сокращения. В третьих, почему новое «слово» ln (да, не слово, а сокращение) начинается с маленькой буквы. Не знаю как после WriteLine на этот runtime можно перейти будучи в здравом уме и при трезвой памяти.


    1. konsoletyper
      18.04.2016 15:10

      Совершенно не понятно зачем на VM тащить мусор вроде inline. Это сугубо работа компилятора и нечего лезть к нему со своими указами. Если нужно выжимать максимум, для этого есть куда более подходящие языки программирования.

      тут


      1. mrigi
        18.04.2016 15:20

        На мой взгляд это означает лишь одно — разработчики столкнулись с трудностями реализации своих задумок и просто добавили ручной костыль. Не спортивно это.


        1. konsoletyper
          18.04.2016 15:54

          Можно подробнее про ручной костыль, задумки и трудности?


    1. voddan
      18.04.2016 17:17

      Спецификатор `inline` в Котлин гораздо сложнее чем например в C++. Он меняет семантику функции, например влияет на вывод типов, поведение generic параметров или на вид публичного Java-api. Прирост быстродействия в случае с лямбдами это только один из эффектов.


  1. WayMax
    18.04.2016 15:06

    Синтаксис не понравился. Считаю его некрасивым. Обилие двоеточий вообще кошмар.


    1. gaelpa
      18.04.2016 17:54

      О вкусах не спорят, но что конкретно показалось некрасивым в синтаксисе котлина?


      1. WayMax
        19.04.2016 09:32

        То что мне не понравилось кому-то другому может показаться наилучшими решениями в синтаксисе. Я только высказал свое личное и субъективное мнение. Не нравиться обилие «мусора»: двоеточия (ассоциации с нелюбимым паскалем), var (я и без этого ключевого слова вижу что тут переменная), fun (я и без этого ключевого слова вижу что тут функция). Не нравится отсутствие точек с запятой (для меня это как «точка» в обычных предложениях). Так же для меня нелогичным является то что тип переменной идет после ее имени.


        1. Sirikid
          19.04.2016 16:14
          +1

          FYI Такой синтаксис позволяет читать объявления слева направо и его проще парсить.


          1. WayMax
            20.04.2016 09:14

            Я же говорю что это мое личное мнение и лично мне удобнее читать объявления слева направо как: «строка с именем Name», «константа с именем Pi» и т.д., а не «есть переменная хз какого типа с именем Name, а вот увидел ее тип строка, ну наконец-то стало понятно».
            Про «парсить» вообще смешно — ЯП нужны чтобы ЛЮДЯМ было удобно писать, а главное ЧИТАТЬ.


            1. lair
              20.04.2016 09:37

              А зачем вам вообще знать типы переменных при чтении? Обычно намного важнее семантика… или вы семантику выражаете типом? Хотя если у вас строки — то вряд ли.

              (это же старый спор про implicit typing vs explicit typing)


              1. WayMax
                20.04.2016 11:08

                Вот вы же и возобновляете этот спор. Я здесь ни с кем не спорил, не призывал, не отстаивал свою точку зрения, а только высказал свое мнение в нейтральной форме.
                Вы же меня завуалировано оскорбляете и пытаетесь доказать что я не прав.

                И да, если бы я в своем примере заменил слово «строка» на некий класс и расписал его на паре страниц кода — то вы бы не стали завуалировано меня оскорблять?


                1. lair
                  20.04.2016 12:25

                  Я здесь ни с кем не спорил, не призывал, не отстаивал свою точку зрения, а только высказал свое мнение в нейтральной форме.

                  Вот и я высказываю свое мнение — пусть и в форме вопроса.

                  Вы же меня завуалировано оскорбляете

                  О нет, ни капли.

                  пытаетесь доказать что я не прав.

                  Нет, я пытаюсь понять, зачем вам нужно то, что вы считаете вам более удобным. Это вообще полезно иногда, причем для обеих сторон.


            1. Sirikid
              20.04.2016 13:47
              -3

              Поэтому я написал FYI, а разработчики языков тоже люди и им не хочется разбирать головоломные синтаксисы. Могу показать пару примеров того что Java/С абсолютно нечитаемый язык даже для людей.


              1. WayMax
                20.04.2016 14:08

                Меня как «пользователя» ЯП не должны волновать трудности его разработчиков.

                Давайте представим себе сценку:
                я разработчик, ко мне приходит заказчик, и говорит: «нарисуй мне 7 красных линий», а я ему в ответ: «мне не хочется разбирать головоломные синтаксисы, давай я нарисую 7 синих — мне так проще».

                И что сделает заказчик? Он найдет того кто нарисует ему красные линии, потому что ему нужны красные, ему так удобно, ему просто так хочется в конце концов и его не волнует (и НЕ ДОЛЖНО волновать) то с какими трудностями сталкивается разработчик. И объяснять разработчику почему именно красных и именно 7 — он не обязан, и искать компромиссное решение (7 фиолетовых линий) тоже не обязан.

                А нечитаемым можно сделать абсолютно любой язык (взять, например, тот же «падонкафский» русский), так что давайте не будем об этом.


  1. guai
    18.04.2016 18:49
    +1

    Я б не стал.
    Раз уж переводить проект на что-то новое, хочется фичей по максимуму, и скала и цейлон в этом плане маячат далеко на горизонте, цейлон даже скорее за горизонтом.
    Отдаёт вендорлоком, внятных планов развития — искал — не нашел.
    Когда в последний раз заглядывал в спеку, был разочарован ее полнотой/качеством.
    Сомнительные решения по синтаксису. Раз уж они, очевидно, нацеливались на аудиторию ява-кодеров, зачем было трогать то, что и в яве не было поломано?..
    Сложилось впечатление, что команда разрабов слабовата, также сложилось впечатление что развитие в какой-то момент замерло, и сдвинул его с мертвой точки Росс Тэйт, создав им более-менее вменяемую систему типов. Он же, к слову, делал систему типов цейлона, но там всё гораздо красивее и мощнее. Изначально анонсировали фичи, которые потом не потянули — тоже добавляет сомнений в профессионализме команды именно в плане дизайна языков.
    Хотя, конечно, если выбирать между явой и котлином, уверен, на котлине приятнее писать будет.


  1. riko
    18.04.2016 19:56

    fun f2(a: Int): Int {
    return f1(a, 2)
    }

    fun f1(): Int {
    return f2(1)
    }


    если не ошибаюсь, f2 должна возвращать f3, а не f1


  1. Nagg
    18.04.2016 23:28
    -1

    >>Следствием упрощения по сравнению со Scala стали также более быстрая компиляция и лучшая поддержка языка в IDE.
    Что-то и время комплиции и поддержка IDE до сих пор в котлине хромает.


  1. int02h
    19.04.2016 11:34
    -3

    Кто-нибудь уже использовал Kotlin в своих проектах? Что там с производительностью? Говорят, что хуже даже, чем у Java. Интересно было бы услышать реальные отзывы или посмотреть на бенчмарки.


    1. UbuRus
      19.04.2016 18:26
      +3

      Джава тормозит? Можно поинтересоваться, что не тормозит?


      1. int02h
        19.04.2016 18:46
        -4

        С++, например. Я просто интересуюсь производительностью Kotlin. Я не готов разводить холивары, просто хотел узнать мнение опытных людей.


        1. UbuRus
          20.04.2016 00:29

          Ну мое мнение: C++ тормозит, да еще и падает с сегфолтами. А если серьезно то Kotlin генерирует байткод очень похожий на байкод языка Java, т.е. перформанс тут не будет отличаться от Java. Но т.к. в Kotlin например можно заинлайнить лямбду, то он даже выигрывает в производительности. И таких мелких примеров много, и еще больше будет (я очень жду связки when + sealed class).

          На jpoint и jet будут доклады от Дмитрия Жемерова про производительность Kotlin с JMH бенчмарками, рекомендую.