Tym razem też uciekniemy w awangardę, ale przyszłości. Przyznam się, że jak usłyszałem “nowy język, z duchem czasu” parę miesięcy temu, to nie zrobiło to na mnie już większego wrażenia. Ot, wyobraziłem sobie mieszankę czegoś pomiędzy TypeScript a C#, no może jeszcze okraszoną jakimiś dziwactwami z Objective-C, którego mało co wtedy kojarzyłem.
No bo, co jeszcze można wymyśleć na tym świecie? Po co wymyślać kolejny nowy język, kiedy Javascript przeżywa swój renesans i pozwala pisać w zasadzie już sporą większość aplikacji? Apple jednak nie poddał się zwątpieniu i zaproponował na swoją platformę zupełnie nowy język. Strzał w kolano w erze Web? Niekoniecznie. Jakby już nikt niczego nie proponował, wszyscy płynęli zgodnie już tylko w mainstreamie JavaScript-owym byłoby zwyczajnie po prostu nudno, a uleganie pewnej zgodności wstecz obniżałoby w pewnych obszarach kreatywność. Może to kwestia polityki, by nie bratać się z mainstreamem i pokazać, że można wymyśleć coś lepszego swojego? Taka pewna alienacja, podkreślenie elitarności, unikalności. Apple przez tyle lat wytrwale trzymało się Objective-C nie ulegając tendencjom światowym. Swift pozwala wywoływać kod Objective-C i vice versa, to z pewnością było istotne dla twórców.
Pomijając już te z lekka filozoficzne rozważania muszę przyznać, że Swift mnie … zaskoczył. Nie zapoznałem się z nim jeszcze nawet w połowie, a zobaczyłem oprócz adaptacji pewnych idei z TypeScript czy C# całkiem sporo świeżych, nowych pomysłów, by na elementy używane powszechnie w programowaniu spojrzeć w jeszcze bardziej otwarty, elastyczny sposób i uprościć sobie dodatkowo pracę. Swift stara się być maksymalnie nieprzegadany, wymaga na ogół jeszcze mniej pisania niż C# czy JavaScript, a kompilator ma olbrzymią inteligencję, że mysz się nie przeciśnie. Swift jest też zaprzeczeniem dynamiczności, zawsze typ jest znany, nie jest to język skryptowy. Przejdźmy do bardziej konkretnych wrażeń
- wnioskowanie typów - jak na razie kojarzy mi się bardziej z C# niż TypeScript
- operatory zakresu - czemu współczesne języki o tym zapomniały? proponowana forma jest całkiem wygodna i do pętli for jak ulał
- interpolacja stringów - analogiczna do C# 6, tylko znaczki nieco inne
- stałe jako stałe i elementy read-only
- propercje klasy jak definicje stałych i zmiennych
- brak ;
- tworzenie nowego obiektu bez słowa new
- elementy opcjonalne - dość popularny ? na końcu typu, jakby typ nullowalny w C#, ale bez ograniczenia typu, typ z wykrzyknikiem zawsze wypakowuje prostą wartość z siebie automatycznie
- warunki if bez nawiasów
- warunkowe przypisanie do zmiennej i operacje na niej - kojarzy się odrobinę z operatorem ? w C# 6, C# 6 robi to jednak jeszcze oszczędniej
- funkcje także poza klasami - jakby ukłon w kierunku C/C++/JavaScript, ale i C# 6 poprzez import klasy statycznej jakby też chciał zrobić ukłon w tę stronę
- słowo func i strzałka w definicji typu zwracanego przez funkcję pewnym udziwnieniem, a może chodziło a nawiązanie formą do modnych lambda expressions? Ale nadal to są funkcje, nie lambda expressions
- słowo class przy funkcji w klasie? To metoda klasowa (czyli statyczna). Podobnie jak w TypeScript mogą istnieć obok siebie niezależnie metoda instancyjna i statyczna o tych samych nazwach.
- wymyślił ktoś wcześniej definiowanie parametrów funkcji z nazwami widzianymi na zewnątrz i wewnątrz ?
- klauzule - jakby takie lambda expressions jak dla mnie, tylko to słowo in trochę może niepotrzebne, natomiast warianty skracania składni schodzą do jeszcze większego skrótu niż w C#. Żeby bardziej zamieszać: jeśli klauzula jest ostatnim parametrem funkcji, to może być wyciągnięta poza jej listę argumentów przy wywoływaniu
- parametry z domyślną wartością - pomysł z C# i TypeScript
- struktury kojarzą się ze strukturami C#, ale niespodzianką jest fakt, że Int, Bool, String, Array, Dictionary są strukturami (zwłaszcza te dwie ostatnie pozycje mogą zaskakiwać kopiowaniem)
- w strukturach jest używane słowo static dla elementów statycznych
- typy wyliczeniowe - śmiała rewolucja w porównaniu do innych języków, tutaj nie dość, że mają dziwny zapis w definicji kojarzący się z blokiem switch case, to mogą mieć w sobie metody i właściwości! Zamiast teraz pisać wszędzie bloki switch case możemy niektóre logiki, konwersje, tworzenie zamknąć w implementacji danego typu. Jeśli chodzi o mapowanie na wartości proste znowu mamy liberalizm, nie musi to być tylko Int, ale także inne typy np. String z podanymi przez nas wartościami.
- tuples - typowe dla współczesnych języków takich jak C#, a ostatnio także JavaScript / TypeScript, formą bardziej kojarzą mi się z tymi drugimi
- protokoły - jak interfejsy, mogą zawierać elementy opcjonalne jak TypeScript i Objective-C, słowo optional przy składowej oraz wymóg stosowania wtedy oznaczenia @objc przy całym protokole burzą trochę spójność i nowoczesność języka, ja dałbym tylko przy składowej ? jak w TypeScript
- wywołanie metody opcjonalnie zaimplementowanej z ? wywoła ją tylko wtedy, jeśli została zaimplementowana. Czy nie jest to piękne? Może trochę kojarzyć się z C# 6, gdzie operator ? wywołuje składową obiektu, jeśli ta nie jest nullem. Dodatkowo możemy wymusić wywołanie metody i uśpić kompilator stosując znak !
- rozszerzenia - są jakby nawiązaniem do extension methods z C#, ale są znacznie czymś więcej, kolejną rewolucją w tej dziedzinie. Otóż możemy rozszerzyć istniejącą już definicję obiektu nie tylko o metody, ale także o wyliczane właściwości, definicje typów zagnieżdżonych czy implementację wskazanego protokołu (interfejsu). Sami twórcy w swoich definicjach mocno tego używają np. w przypadku String.
Jeśli zainteresowały Cię moje zdania o Swift powyżej, możesz przeczytać notatki o tym języku poniżej lub samemu zacząć go rozpoznawać. W Swift odnajdziemy z jednej strony pomysły z innych języków z ostatnich lat oraz te najnowsze, z drugiej strony sporo nowych, czasami wręcz śmiałych propozycji, które mogą z kolei zainspirować twórców C# czy JavaScript.
Intro
Swift
- przyszłość na platformie Apple
- zerwanie z C
- połączenie możliwości nowoczesnych języków
- zwięzły
- ścisle typowany
- nie jest językiem skryptowym
- nie jest dynamicznie typowany
- nie wszystkie API zostanie przepisane
- kodu z Objective-C nie można konwertować do Swift, ale można wywoływać z jego poziomu i na odwrót
- w jednym projekcie można łączyć oba języki
Obietnice Swift
- bezpieczeństwo
- elastyczność
- produktywność
- nowoczesność
- szybkość
- łatwość nauki
Swift REPL
xcrun swift
import Foundation
let df = NSDateFormatter() //jak alloc
df.dateStyle = .MediumStyle // NSDateFormatterMediumStyle
df.stringFromDate(NSDate())
^+P lub strzałka do góry – poprzednia komenda
:quit lub CTRL+D – wyjście
Można też zapisać komendy do pliku *.swift i go wykonać:
xcrun swift plik.swift //kompiluje plik i uruchamia plik wykonywalny
println(df.stringFromDate(NSDate()))
XCode playground - wyniki po prawej stronie okna
import UIKit
var str = “abc”
var x = 1 // Swift wnioskuje typy tam, gdzie jest to możliwe
for i in 1…10 { // 1…10 - operator zakresu
x = i * 2
println(“x = \(x)”) //interpolacja stringów, przypomina tą z C# 6 tylko że ma () zamiast {}
}
Można też testować elementy wizualne
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 120, height: 50))
label.backgroundColor = UIColor.redColor()
label.sizeToFit()
Typy danych
W każdym momencie typ obiektu jest znany
Podstawowe elementy
- klasy
- protokoły
- rozszerzenia
- struktury
- krotki (tuples)
- funkcje
- closures
- wyliczenia
let - stała, bez możliwości ponownego przypisania czy modyfikacji (tablice)
var - zmienna, możliwość zmiany
Objective-C przez typ: NSArray, NSMutableArray
var array = [“Kapitan Bomba”, “Piesek Leszek”, “Koń Rafał”]
array.append(“Miś”)
class A {
let x: String
var y: String
init(x: String, y: String) {
self.x = x;
self.y = y;
}
}
var a = A(x: “Lepsza Statystyka”, y: “Układ zamknięty”)
a.x = “Czarny czwartek”
Elementy opcjonalne
var x: String? //String lub nil, typ jest cały czas widziany jako opcjonalny string {Some “wartość”} / Optional(“wartość”), nie jako string
var x: String! //specjalny przypadek pozwalający na nil, niejawne wypakowanie wartości
Opcjonalne bindowanie
Objective-C:
NSFileManager *fm = [NSFileManager defaultManager];
NSData *data = [fm contentsAtPath: @”/Users/marcin/plik.txt” ];
if (data) {
}
Swift:
let fm = NSFileManager.defaultManager()
if let data = fm.contentsAtPath(“/Users/marcin/plik.txt”) {
}
if x != nil {
}
x! - wymuszenie wypakowania wartości z typu opcjonalnego
let z = (x ?? “brak”) //wypakowanie zawartości jak inna wartość niż nil
Każdy typ może być opcjonalny
Typy wartości i referencji
Typy referencyjne
- klasy
- funkcje
func printAlbum(a: Album) -> Void { //strzałkę z Void można pominąć
println(“\(a.artist) \(a.title)”)
}
Typy wartości
- struktury
- wyliczenia
- krotki
Klasy i obiekty
- pojedyncze dziedziczenie
- polimorfizm
- klasowe i instancyjne metody i propercje
class A {
let x: String
var y: String
init(x: String, y: String) {
self.x = x;
self.y = y;
}
func f(message: String) {
}
class func f(message: String) { //metoda klasowa
}
}
let a = A(x: “Lepsza Statystyka”, y: “Układ zamknięty”)
a.f(“Przepraszam, czy tu biją?”)
A.f(“Miś”)
Funkcje i klauzule
let array = [“Kapitan Bomba”, “Piesek Leszek”, “Koń Rafał”]
//klauzula
array.filter({(name: String) -> Bool in
return name.hasPrefix(“K”)
})
func f(name n: String, age a: Int) - wariant z zewnętrzymi i wewnętrznymi nazwami parametrów, pierwsze są nazwy widziane na zewnątrz
func f(#name: String, #age: Int) - zewnętrzne i wewnętrzne nazwy parametrów są takie same, trzeba wtedy w wywołaniu funkcji jawnie użyć nazw parametrów
f(name: “Zuch Kozioł”, age: 32)
Przeładowania funkcji na podstawie listy argumentów
Uproszczenia klauzul
array.filter({(name) -> Bool in
return name.hasPrefix(“K”)
}
array.filter({ name -> Bool in
return name.hasPrefix(“K”)
}
array.filter({ name -> Bool in
return name.hasPrefix(“K”)
}
array.filter({ name -> Bool in
name.hasPrefix(“K”)
}
array.filter({ $0.hasPrefix(“K”)}
Jeśli klauzula jest ostatnim parametrem funkcji, to może być wyciągnięta poza jej listę argumentów
array.filter() { (name) -> Bool in
return name.hasPrefix(“K”)
}
Taka składnia jest możliwa dla wszystkich czterech wariantów definiowania klauzul.
Zamiast definiowania klauzuli można do filter przekazać nazwę funkcji o odpowiedniej sygnaturze.
Funkcja może zwracać klauzulę:
func nameWithMaxLength(max: Int = 3) –> (String) -> Bool {
return { (name: String) in
return name.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) < max
}
}
Przy argumentach z domyślnymi wartościami, jeśli decydujemy się określić w wywołaniu funkcji ich wartości, należy podać także ich nazwy
array.filter(nameWithMaxLength(max: 5))
Różnice między metodą a funkcją:
metoda
- dostępna przez self
- pierwszy argument - domyślna lokalna nazwa
- kolejne argumenty - lokalne i zewnętrzne nazwy
Struktury
- właściwości
- metody
- dostęp przez indeksy
- inicjalizatory
- rozszerzalność
- zgodność z protokołem
Klasy
- to, co struktury
- dziedziczenie
- polimorfizm
- deinicjalizatory
- liczenie referencji
struct Point {
var x: Int
var y: Int
}
Domyślny inicjalizator
var p = Point(x: 10, y: 20)
Strukturami są:
- Array<T>
- Dictionary<Key: Hashable, Value>
- Bool
- String
- Int
struct X {
static var x: String { get } //propercja statyczna, tylko do odczytu
}
Wyliczenia
Objective-C: makro
typedef NS_ENUM(NSInteger, Xyz) {
XyzOption1,
XyzOption2,
XyzOption3
};
Swift:
enum Xyz: Int { //: Int można pominąć
case Option1
case Option2
case Option3
}
Wyliczenia mogą zawierać:
- metody
- właściwości
- surowe wartości
UIView.animationCurve = UIViewAnimationCurve.EaseInOut
lub krócej:
UIView.animationCurve = .EaseInOut
enum Xyz: String {
case Option1 = “Option1”
case Option2 = “Option2”
case Option3 = “Option3”
private func regularExpression() –> NSRegularExpression {
switch self {
case .Option1:
return NSRegularExpression(pattern: “…”, options: nil, error: nil)
case .Option2:
…
case .Option3:
…
}
}
func isValidFor(xyz: String) -> Bool {
}
static func from(xyz: String) -> Xyz {
}
}
Xyz.from(“Option1”).toRaw() //konwersja z napisu na enum, a potem enum na wartość
0..< 10 - w połowie domknięty operator zakresu, nie zawiera 10
Tuples
var x = (code: 500, message: “Error”) //para (Int, String)
x.code lub x.0
x.message lub x.1
(Int?, String?)
(Int, String) ?
Protokoły
protocol A {
var x: Double { get }
func f()
}
class B: A {
var x: Double = 100
func () {
…
}
}
let array: [A] = [b, c]
@objc protocol A { //@objc - interoperabilność z Objective-C, w przypadku definiowania opcjonalnych wymagane
var x: Double { get }
func f()
optional func g(h: Double) //funkcje opcjonalne w protokołach jak w Objective-C
}
a.g?(20) //wywołanie opcjonalnej metody, jeśli została zaimplementowana
a.g!(20) //wymuszenie (błąd w run-time)
Rozszerzenia
- wyliczane właściwości
- nowe metody
- definicja typów zagnieżdżonych
- zgodność z protokołem
- istniejące zachowania nie mogą być nadpisane
extension String {
func reverse() –> String {
let chars = Array(self).reverse()
…
}
}
let song = “Karinga”
song.reverse()
extension String: CollectionType {
struct Index: … {
…
}
//implementacja metod
}
Brak komentarzy:
Prześlij komentarz