Здравствуйте, уважаемые читатели!

Спасибо, что практически смели с полок нашу экспериментальную книгу по языку Swift. Серьезно обдумываем допечатку в апреле, консультируемся с автором насчет обновлений, а также подыскиваем другие, более фундаментальные книги. В ходе этих поисков довелось познакомиться с отличной статьей, которую можно было бы назвать «Что Swift грядущий нам готовит». Ее перевод найдете под катом.

Приятного чтения!


Выход языка Swift 2.2 запланирован вместе с выпуском Xcode 7.3 и должен состояться в период с марта по май 2016. Эта версия позиционируется как промежуточная между Swift 2 и Swift 3 и предполагает немало изменений. В принципе, Swift 2.2 просто предупреждает вас об изменениях, тогда как Swift 3 не позволит делать многих вещей, к которым вы привыкли. Но не волнуйтесь – думаю, это очередной важный шаг, который поможет сделать Swift еще свифтее.

Здесь я упомяну несколько важнейших изменений, планируемых в Swift. Можете опробовать их прямо сейчас, скачав демонстрационную версию Swift 2.2, которая будет работать с Xcode 7.2 и 7.3-бета, если немного похимичить. Предварительно потребуется просто познакомиться с последней версией Swift, описанной на официальном сайте.

Операторы

В Swift 2.2 не будет операторов ++ и --. На первый взгляд меня это возмутило, но, фактически, в современном Swift они уже просто не нужны. Необходимость в инкрементах отпадает благодаря новому циклу for и функциям высшего порядка вроде map или filter. Разумеется, вы по-прежнему можете реализовывать инкремент сами, но уже иным способом.

func plusPlus() {
    var i = 0
    i++ // ВНИМАНИЕ: '++' нежелателен: в Swift 3 от него планируется избавиться
    i += 1 // '+=' и '-=' применяется для целочисленных типов или чисел с плавающей точкой 
 
    // succesor() и predecessor() применяются с индексными типами
    let alphabet = "abcdefghijklmnopqrstuvwxyz"
    let index = alphabet.characters.indexOf("u")
    if let index = index {
        alphabet.characters[index.successor()]
    }
}


Крис Латтнер считает, что эти операторы инкремента/декремента лишь вносят путаницу, особенно если существуют сразу в двух формах: префиксной и постфиксной. На самом деле, я и не помню, когда в последний раз приходилось использовать ++x, поэтому если от них избавиться, то Swift, возможно, упростится в изучении как первый язык программирования.

C-подобные циклы

Такие унаследованные циклы – еще одна вещь, заимствованная из C. Удаляя for init; comparison; increment {}, мы также можем с легкостью удалить ++ и --. Но не переживайте – в Swift есть отличный и очень удобный цикл for-in.

func forLoop() {
    // ВНИМАНИЕ: C-подобная инструкция for нежелательна
    // и будет удалена в следующей версии Swift
    for var i = 1; i <= 10; i += 1 {
        print("I'm number \(i)")
    }
    
    // Инновационный стиль Swift работает нормально:
    for i in 1...10 {
        print("I'm number \(i)")
    }
}



Срезы

Вот чего я ждал целую вечность. Массивы в Swift 2.2 имеют функцию removeFirst()
. Ее можно было без труда написать и самому в качестве расширения, но теперь она предоставляется из коробки. Функция очень умная, и, даже хотя ваш массив может быть переполнен опционалами, она просто удаляет первый элемент, а не оставляет его в виде nil
. Обожаю.

func slices() {
    var array = ["First", "Second", "Third", "Fourth"]
    array.removeLast() // она существовала всегда, но…
    array.removeFirst() // родилась заново (каждый n-ный элемент становится n-1)
}


Сравнение кортежей

Может быть, это и не сенсация, но странно, что такая возможность до сих пор отсутствовала. В Swift 2.2 и далее можно сравнивать кортежи, содержащие до 6 элементов Equatable
.

func tuples() {
    let tuple1 = (1, true, 3, 4, 5, "a")
    let tuple2 = (1, false, 3, 4, 5, "a")
    let equalTuples = tuple1 == tuple2
    print(equalTuples) // false
}


Больше никакого карринга

Возможно, вы расстроитесь, если по-настоящему любили каррированные функции, но я не расстроился. В Swift 2.2 они переосмыслены и станут немного более удобочитаемыми.

// ВНИМАНИЕ: синтаксис объявления каррированных функций будет упразднен 
// в новой версии Swift; пользуйтесь списком с одним параметром
func noMoreCurryingThisWay(a: Int)(b: Int) -> Int {
    return a + b
}
 
func curryThisWay(a: Int) -> (Int) -> Int {
    return { (b: Int) -> Int in
        return a + b
    }
}



Проверка версии Swift

Очень интересная возможность, правда, надеюсь, не придется часто ею пользоваться. Я предпочитаю рефакторить код, совместимый со Swift 2, до Swift 2.2, а не поддерживать сразу две (или более) версий. Но в некоторых случаях эта возможность может пригодиться (например, пишем каркас, который должен работать и с 2.0, и с 2.2 – а, возможно, даже с 3.0). Попробуйте.

func swiftVer() {
    var cuteNumber = 7
    #if swift(>=2.2)
        print("Good morning, I'm brand new Swift")
        cuteNumber += 1
    #else
        print("Hi, I'm elder than my brother, but can ++")
        cuteNumber++
    #endif
    
    // Такой стыд, '#if swift' выглядит как старомодный макрос из Obj-C,
    // а не по-свифтовски как '#available'. Обратите внимание на этот префикс '#'.
    // Выглядит старомодно, а я хотел бы, чтобы было вот так:
    /*
     
    if #swift(>=2.2) {
    } else {
    }
     
    */
}


Селектор

Еще один макро-прием, которого я избегаю. В Swift 2.2 нельзя указывать селектор как строку. В открытом обсуждении был выбран вариант #selector
, а не Selector()
, что напоминает создание экземпляра (и также нежелательно). Фактически, этот вариант все равно лучше, чем просто строка, благодаря автозаершению; вероятность опечатки минимальна.

func selector() {
    let button = UIButton()
    // ВНИМАНИЕ: использование строкового литерала для селекторов Objective-C
    // нежелательно; вместо этого используйте '#selector' 
    button.addTarget(self, action: "forLoop", forControlEvents: .TouchUpInside)
    
    // На самом деле '#selector' работает нормально
    button.addTarget(self, action: #selector(forLoop), forControlEvents: .TouchUpInside)
}


Заключение

Это не все изменения, ожидаемые в Swift 2.2, но, на мой взгляд, наиболее важные. Рекомендую заглядывать в официальный свифтовский журнал изменений (правда, на деле это всего лишь «дорожная карта») и в раздел swift-evolution на GitHub Apple, где озвучиваются все предложения об эволюции языка.

Меня очень вдохновляет такое развитие событий, я предвкушаю грядущие изменения. Swift современный и крутой. Приходится быть современным, если хочешь перерасти Obj-C. Нужна решимость, чтобы избавиться от этих C-подобных циклов, использующихся с 1972 года. Будем надеяться на лучшее!

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


  1. Gorthauer87
    24.02.2016 18:16

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


  1. Error1024
    24.02.2016 18:17

    Эм… Apple убирают из языка ++ / --, for(;;) — они решили превратить swift в Object Pascal? :)


    1. abjurato
      24.02.2016 21:56

      Останутся += / -= и for-in.

      Причем последний можно использовать с массивами:
      for i in 1...5

      а массивы можно создавать с помощью stride:
      for i in 1.stride(to:5, by: 1)


      1. Error1024
        25.02.2016 01:18

        Дык в object Pascal также можно:

        for i in [1..5] do


    1. mjr27
      25.02.2016 10:48
      +1

      Бред, конечно.
      Циклы с счетчиком, изменяемым внутри тела — обычное дело в общем-то. В том же Python каждый раз эмулировать такое через while достало неимоверно.


  1. RedRover
    24.02.2016 18:33

    Swift современный и крутой. Приходится быть современным, если хочешь перерасти Obj-C. Нужна решимость, чтобы избавиться от этих C-подобных циклов, использующихся с 1972 года. Будем надеяться на лучшее!

    Я вот например считаю что перерастать Objective-C не требуется, это не некая устаревшая технология, а отличный инструмент со своими сильными и слабыми сторонами, как и Swift.

    Хотя отказ от c-style циклов и инкрементов имеет под собой основу: инкремент в Swift представляет собой чисто синтаксическую возможность и x++ и x += 1 по сути ничем не различаются.
    С циклами все более странно, в принципе в классических for циклах нет ничего плохого, но реализованы они в Swift так что
    for i in 0 ..< x
    работает быстрее чем
    for var i = 0; i < x; i++
    возможно стоит оптимизировать c-style циклы, а не убирать их из языка. Кроме того, for in не поддерживает декремент.


    1. abjurato
      24.02.2016 21:58

      for i in 10.stride(to:5, by: -1)


      1. RedRover
        24.02.2016 22:13

        Согласен, но этот цикл по идее должен на каждом шаге вызывать next() проверять что следующий элемент существует и выполнять тело цикла если это так. Что совсем не тоже самое по скорости чем уменьшать значение i на 1 и проверять что оно не стало меньше нуля.


  1. Gorthauer87
    24.02.2016 18:34

    Кстати, там за бортом осталась поддержка swift test, как минимум. Для не OsX платформ это точно актуально.


  1. splav_asv
    24.02.2016 19:11

    > В Swift 2.2 и далее можно сравнивать кортежи, содержащие до 6 элементов Equatable
    Почему именно 6 интересно, зачем было делать это магическое число и можно ли сравнивать вложенные кортежи?


    1. PapaBubaDiop
      24.02.2016 19:59
      +3

      С выходом 7-ого iPhone число элементов будет ++. Пардон, += 1


    1. sulnedinfind
      24.02.2016 21:28

      Не знаю как в Swift, но в C# количество элементов кортежа (тип Tuple) тоже ограничено.
      В C# это связано с тем, что нет генериков с переменным числом параметров, и вообще вариадики поддерживаются только гомогенные (т. е. одного типа). Поэтому в C# определены сразу несколько типов Tuple: с одним, двумя итд. параметрами.
      Если мне память не изменяет, в Swift система типов устроена сходным образом (нет генериков-вариадиков).


      1. impwx
        24.02.2016 21:43

        Кстати говоря, еще ни разу не видел оправданного использования Tuple в C#. Обычно его используют только потому, что лень создавать именованный класс, но чтение и рефакторинг такого кода превращается в пытку. Поэтому обычно лучше использовать анонимные классы с именованными полями, или же не лениться и объявить класс, если его нужно пробрасывать между методами. В крайнем случае для 2 элементов сойдет KeyValuePair.


        1. sulnedinfind
          24.02.2016 21:45

          Я тоже как-то не встречал. Пару раз пытался по опыту других языков их воткнуть, но получалось уж слишком монструозно :)


          1. CepbluBoJlk
            24.02.2016 22:48

            Без синтаксической поддержки языка это не более чем просто класс, аналогично не нашёл применения, в контексте метода куда удобнее анонимные типы, в контексте класса — вложенные классы. К тому же отсутсвие вывода типов в конструкторах зашумляет код. Радует что в списке фич 7 версии языка находится в разделе "Strong interest"


  1. NeoCode
    24.02.2016 19:13
    +5

    Что-то все повыкидывали.
    Хоть я и не писал на Swift, но тенденция выкидывать общепринятые устоявшиеся за десятилетия и понятные любому программисту конструкции (типа инкремента) ИМХО странная. Это все равно что из математики выкинуть значок квадратного корня, сославшись на то что можно пользоваться возведением в степень 1/2.


    1. sulnedinfind
      24.02.2016 22:00
      +2

      В отличие от математики, программный код разбирает компьютер, а находящиеся по эту сторону монитора не желают ни вводить с клавиатуры длинные последовательности, ни ждать, пока ИИ сможет понимать код, написанный на математическом языке.
      Каждая фича в языке, каждая строчка в компиляторе может стоить немалых денег в поддержке. "Общепринятые" вещи из Си отсутствуют во многих языках, на которых выросло поколение авторов Swift, языков, начавших активно выходить за пределы академической среды — функциональных языков, в основном.
      Swift — очень современный в этом плане язык, авторы активно движут его к зарекомендовавшим себя и модным ныне практикам ФП, но при этом в нем слишком много фич (а особенно синтаксиса) — скорее всего поэтому авторы убирают все, что в их идеальном стиле не используется, но при этом не ограничивает круг решаемых задач.
      Важно понимать, что идеология Swift подразумевает написание идеоматического высокоуровнего высокодекларативного кода, который затем трансформируется очень умным компилятором в эффективный машинный код, который предположительно должен быть таким же быстрым, как написанный с использованием таких фич, как инкремент и цикл for в стиле Си.


      1. NeoCode
        25.02.2016 10:15

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


        1. druidvav
          25.02.2016 11:40
          +1

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


          1. NeoCode
            25.02.2016 16:03

            Да я все это прекрасно понимаю…
            Но жить-то станет скучнее, программирование превратится из Хакерства в унылую конвейерную работу за копейки. Мечта менеждеров, да… но программистам-то это зачем?


            1. druidvav
              25.02.2016 16:05

              Каждому делу свой инструмент только и всего :) Хакерство и творчество никуда не денется. Всегда нужны и художники и маляры, а инструменты у них в чем-то похожи, но очень разные.


          1. Source
            02.03.2016 23:46

            А потом другим программистам наслаждаться поддержкой кода таких творцов.

            Вы всерьёз считаете, что конструкции типа
            for i in 1000000.stride(to:1, by: -1)
            читаемы и удобнее в поддержке, чем примитивный for-цикл?
            Или что операторы инкремента/декремента могут стрелять по ногам?
            Понятно, что авторы языка решают что оставлять, но аргументы на тему, что конеретно эти изменения хоть как-то способны улучшить качество кода, выглядят очень странно.
            Ладно бы все, как один, написали: фиг с ним с for, будем хвостовую рекурсию использовать… но нет, все про этот жуткий вариант со stride, который ещё и память на абсолютно ненужную последовательность тратить будет.


            1. abjurato
              03.03.2016 00:22
              +2

              Это не все, это я сразу в несколько веток ответил, потому что страйд не самая широко известная штука.
              А касательно памяти — вы уверены, что нет никакой оптимизации на уровне компилятора?


              1. Source
                03.03.2016 14:34

                stride — это способ генерации последовательности/массива, поэтому использовать его для замены счётчика итераций значит использовать его не по назначению. Я не уверен в отсутствии оптимизаций на этот случай на уровне компилятора, но, честно говоря, присутствие таких оптимизаций было бы нелогичным. Классический цикл for отсутствует во многих функциональных языках, но в любом из них генерация списка чисел для обхода считается моветоном.


  1. impwx
    24.02.2016 20:27
    +3

    Вопросов больше, чем ответов.

    • Как без С-style цикла сделать итерацию с шагом, отличным от 1?
    • Почему функция `removeFirst` это срез? Общепринятый смысл термина «срез» — это подмножество массива.
    • Зачем убирать более удобную версию записи каррирования? Новый синтаксис с 3 и более параметрами будет выглядеть монструозно.
    • Почему сравнивать можно только кортежи до 6 элементов?


    1. NeoCode
      24.02.2016 20:52

      Фичу с кортежами небось прикрутили сбоку изолентой :). В C# вроде бы максимальная длина кортежа равна 8, здесь 6…
      Догадайтесь почему и откуда такие магические числа.


      1. impwx
        24.02.2016 21:37

        В C# есть несколько стандартных классов типа Func, Action и Tuple, у которых есть "варианты" с различным количеством аргументов. Причина в том, что когда в .NET 2.0 добавили обобщения, особой необходимости в variadic type arguments еще не было, а потом добавить это без кардинального изменения рантайма оказалось невозможно. При необходимости кортеж произвольной длины можно создать, используя в качестве последнего элемента вложенный кортеж. И тогда они будут-таки рекурсивно сравниваться, сколько их ни навтыкай.

        Однако вопрос в другом. В Swift вообще нельзя сделать кортеж из более, чем 6 элементов? Или все же можно, но он не будет сравниваться? Если так, то это однозначно сознательное ограничение, но его смысл от меня ускользает. Производительность?


    1. sulnedinfind
      24.02.2016 21:43
      +1

      Как без С-style цикла сделать итерацию с шагом, отличным от 1?

      Скорее всего, авторы хотят сделать акцент на техниках функционального программирования и эти юзкейсы должны покрываться фильтром по индексу, например (псевдокод): collection.filter(eventh).map(doSomething).reduce(calcResult)

      Зачем убирать более удобную версию записи каррирования? Новый синтаксис с 3 и более параметрами будет выглядеть монструозно.

      В Swift слишком много синтаксиса. Скорее всего, синтаксис каррирования усложнял парсер, так как требовал разрешения неоднозначностей, а может просто прибили как наименее полезный синтаксис по версии авторов.
      "Новый" синтаксис — явная форма возврата функции. На практике я довольно редко вижу применение функции больше, чем в две стадии, так что в каком-то смысле компромисс для меня очевидный.


      1. impwx
        24.02.2016 21:52

        Акцент получается довольно странный. Циклы и инкременты — слишком императивно, каррирование — слишком функционально. Прагматичность — это хорошо, но для языка и платформы ортогональность не менее важны.


        1. sulnedinfind
          24.02.2016 22:08
          +1

          Я думаю, упор не на функциональное программирование как таковое, а скорее на декларативность, достижимую с помощью средств, заимствованных из функционального программирования. Ну и на value semantics. И умный компилятор, который магически улучшает говнокод.
          С таким компилятором, кстати, чем меньше свободы людям, тем больше оптимизатору.
          Получается очень хороший набор базовых постулатов, с синергией. И очень в духе Apple "Мы делаем лучшие чистилки для апельсинов, потому что мы лучше всех знаем, как чистить апельсины." Можно ли чистить красные апельсины? Я думаю, для этого найдется своя Cydia.


      1. abjurato
        24.02.2016 22:01
        +2

        Как без С-style цикла сделать итерацию с шагом, отличным от 1?

        проще
        for i in 100.stride(to:500, by: 25)


        1. Error1024
          25.02.2016 16:51

          Вырвиглазно :(

          for(i = 100; i < 500; i += 25)

          – выглядит проще и компактнее.


    1. Piskov
      25.02.2016 01:13
      +1

      Зачем убирать более удобную версию записи каррирования?

      См. в motivation

      Почему сравнивать можно только кортежи до 6 элементов?

      Чтобы соблюсти баланс между удобством и размером библиотеки. Текущее раширение до 6 увеличило размер на 1,4 %. 12 элементов увелчивают размер на 5,5 % (+175 КБ).

      Почему функция `removeFirst` это срез? Общепринятый смысл термина «срез» — это подмножество массива.

      Тут просто про свифтовое ArraySlice, Sliceable и пр. базовые -slice типы (смысл которых, что они не копируют элементы, а юзают существующую память, отсюда и название и эффективность в O(1)). Вот в них и добавили removeFirst. А обычные массивы все это наследуют по иерархии.


      1. impwx
        25.02.2016 10:20

        Что-то ребята поторопились с дизайном языка.

        Частичное применение функции, имхо, сделали только потому, что это модно. Теперь же оказалось, что никто это не использует потому, что слишком сложно.

        По поводу кортежей на stackoverflow подсказывают, что в него можно засунуть до 1948 (!!!) элементов. Зачем это нужно, особенно если они беспокоятся по поводу размера библиотеки?


    1. Piskov
      25.02.2016 01:34
      +1

      Как без С-style цикла сделать итерацию с шагом, отличным от 1?

      Народ писал про stride, но, положа руку на сердце, я за 10 лет ни разу такой цикл не писал.

      К слову, Андрюх, тут, скорее, можно возразить про более актуальное «а как узнать индекс текущего элемента, если убрали олдскульный for», но помимо классического:
      for item in items { }
      

      …есть менее известный:
      for (index, item) in items.enumerate() { }
      

      В том же шарпе такое только, если не изменяет память в «.Select( (index, item) => { })», а вот в обычном foreach часто не хватает. Свифт, в общем, молодец.


      1. splav_asv
        25.02.2016 08:00

        Прямо дежавю с Python. И это скорее хорошо.


  1. AterCattus
    24.02.2016 21:32
    +1

    Оффтопик. К чему эти точки и запятые в начале строк, а не в конце?


  1. igrishaev
    25.02.2016 08:43

    if swift(>=2.2)

    Разве нельзя было добавить волшебную переменную __version__?


    1. NeoCode
      25.02.2016 11:06
      +1

      Боже мой, что это? Унарный оператор сравнения?


    1. abjurato
      25.02.2016 18:15
      +1

      Это не просто if, а волшебный #if

      Кстати, __такие__ волшебные переменные теперь заменены на #такие
      У Erica Sadun в бложике много интересного.


  1. StrangerInRed
    25.02.2016 11:15

    Не хватает итерирование по массиву с шагом аля

    for button in buttons step 2 {
    }

    или

    for 1..5 : 2 {
    }


  1. Viacheslav01
    25.02.2016 13:15
    +1

    Сказывается любовь к странным повторяемым символам пунктуации, теперь в моде -> вместо []

    func curryThisWay(a: Int) -> (Int) -> Int {
    return { (b: Int) -> Int in
    return a + b
    }
    }


  1. Krypt
    25.02.2016 17:55

    Я уж надеялся, что начиная с 2й версии на нём можно пилить что-то серьёзное и новый проект можно стартовать на Swift. Но нет, подожду-ка я до 4й :)