Loading
Kamen Kotsev
This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.
Камен Коцев
HackConf 2017
Има ли най-хубав език за програмиране?
Програмисти обсъждащи езици за програмиране
характеристика
от процесора
Покрива се от всеки език за програмиране с който бихме работили
за програмиста
Процедурно програмиране
дефиниция
пример
int a = 5;
int b = 7;
int c = 3;
int d = 0;
d = a + b + c;
пример 2
// Fraction A
int numeratorA = 3
int denominatorA = 4
// Fraction B
int numeratorB = 2
int denominatorB = 5
// Fraction C = A + B => 3/4 + 2/5 = 23/20
int numeratorC =
numeratorA * denominatorB +
numeratorB * denominatorA
int denominatorC =
denominatorA * denominatorB
А ако бяха много дроби?
работата с много на брой примитивни типове е трудна и уязвима на грешки
ООП
характеристика
пример
class Fraction {
private int num;
private int den;
Fraction(int num, int den) {
this.num = num; this.den = den;
}
public Fraction add(Fraction other) {
return new Fraction(
this.num * other.den + this.den * other.num,
this.den * other.den
);
}
}
// ...
// C = A + B
Fraction C = A.add(B)
характеристика
Асинхронно = Acync
проблеми
дефиниция (пак)
има ли решения?
дефиниция
Има си своята цена, но е решение!
характеристика
да допуска по-малко грешки при писане
Проблемите най-често идват от писането със страничен ефект (Side effect)
дефиниция
решение
Pure functions - функции без страничен ефект
same input => same output
Можем ли да пишем само с pure functions?
Да, но рядко ще ни е полезно
Можем обаче да изнесем страничния ефект извън бизнес логиката
бонус
Тестват се много лесно!
Same input = Same output
характеристика
Easy to write
+
Immutable data
+
Pure functions
Easy to
write
immutable
data
Pure
functions
Recursion
Higher order
functions
First-class
functions
Scala е мост между процедурните/ооп парадигми и функционалните парадигми
Дава ни инструментите, които са ни нужни, за да пишем на стила, който ни допада/трябва
public class User {
private Integer id;
private String name;
User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
public class User {
private Integer id;
private String name;
User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
public class User {
public Integer id;
public String name;
User(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
public class User(Integer id, String name) {
public Integer id;
public String name;
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
1. Scala compiler infers types when they are obvious
String a = "Hello!"
a = "Hello!"
val a = "Hello!"
or
val a: String = "Hello!"
public class User(Integer id, String name) {
public Integer id;
public String name;
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
2. toString метода зависи само от конструктора и при immutable обекти е ясен
val j = User(1, "John")
print(j) // User(1, John)
val k = User(2, "Kate")
print(k) // User(2, Kate)
public class User(Integer id, String name) {
public Integer id;
public String name;
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
3. Ако указваме типа и имената на атрибутите при извикването на конструктора, и обекта ни ще е immutable -> няма смисъл да ги пишем втори път
public class User(Integer id, String name) {
public Integer id;
public String name;
@Override
public String toString() {
return "User(" + this.id + ", " + this.name + ")";
}
}
4. Ако дефинираме immutable обект можем да приемем че ще се използва във всички пакети освен ако не сме казали нещо друго (Ще е public по default)
case class User(id: Int, name: String)
Immutable обект, с който може да се прави pattern matching
public boolean exists(Integer el, List<Integer> list) {
for(int i=0; i<list.size(); i++) {
if(list.get(i).equals(el)) {
return true;
}
}
return false;
}
def exists(x: Int, l: List[Int]): Boolean = {
if(l.isEmpty)
false
else if(l.head == x)
true
else
exists(x, l.tail)
}
def exists(x: Int, l: List[Int]): Boolean = {
l match {
case Nil => false
case head :: tail =>
if(head == x) true
else exists(x, tail)
}
}
def exists(x: Int, l: List[Int]): Boolean = l match {
case Nil => false
case head :: tail if head == x => true
case _ => exists(x, l.tail)
}
def exists(x: Int, l: List[Int]): Boolean = l match {
case Nil => false
case `x` :: _ => true
case _ => exists(x, l.tail)
}
демо type conversion
case class Name(value: String)
case class User(id: Int, name: Name)
//...
val u = User(1, Name("John"))
демо type conversion
case class Name(value: String)
case class User(id: Int, name: Name)
//...
implicit def stringToName(s: String): Name = Name(s)
val u = User(1, "John") // Ok
демо type conversion
implicit def booleanToInt(b: Boolean): Int =
if(b) 1 else 0
// ...
def addOne(x: Int): Int = x + 1
// ...
addOne(5) // = 6
addOne(true) // = 2
демо
implicit class StringHelpers(x: String) {
def emphasize: String = s"!!!${x.toUpperCase}!!!"
}
val s = "John"
print(s.emphasize) // !!!JOHN!!!
демо
case class User(id: Int, name: String)
val input =
""" { "id": 5, "name": "Jill"} """
JSON.parse(input).as[User]
trait JSValue {
def as[T](implicit r: JSReads[T]) = r.read(this)
}
case class JSObject(value: Seq[(String, JSValue)]) extends JSValue
case class JSNumber(value: BigDecimal) extends JSValue
case class JSString(value: String) extends JSValue
case class JSBoolean(value: Boolean) extends JSValue
case class JSArray(value: Seq[JSValue]) extends JSValue
case object JSNull extends JSValue
trait JSReads[A] {
def read(js: JSValue): JSResult[A]
}
object JSReads {
def simpleReads[A](f: PartialFunction[JSValue, A]): JSReads[A] = JSReads[A] {
f.lift.andThen {
case Some(x) => JSSuccess(x)
case None => JSError("Could not parse")
}
}
implicit val intReads: JSReads[Int] =
simpleReads{ case JSNumber(x) => x.toInt }
implicit val stringReads: JSReads[String] =
simpleReads{ case JSString(x) => x }
implicit val booleanReads: JSReads[Boolean] =
simpleReads{ case JSBoolean(x) => x }
implicit val doubleReads: JSReads[Double] =
simpleReads{ case JSNumber(x) => x.toDouble }
}
object JSON {
def read[T](value: JSValue)(implicit r: JSReads[T]): JSResult[T] =
r.read(value)
def parse(json: String): JSValue = {
json.trim match {
case x if x.startsWith("[") && x.endsWith("]") =>
val children = recogniseJson(x.tail.init)
val res = children.map{case (k,v) => parse(v)}
JSArray(res)
case x if x.startsWith("{") && x.endsWith("}") =>
val children = recogniseJson(x.tail.init)
val res = children.map{case (k,v) => k -> parse(v)}
JSObject(res)
case "null" => JSNull
case x if x.startsWith("\"") && x.endsWith("\"") => JSString(x.tail.init)
case x if x.forall(x => x.isDigit || x == '.') => JSNumber(BigDecimal(x))
case x if x == "true" || x == "false" => JSBoolean(x.toBoolean)
case x => throw new Exception("WTF is " + x)
}
}
// ...
}
object JSON {
def read[T](value: JSValue)(implicit r: JSReads[T]): JSResult[T] =
r.read(value)
def parse(json: String): JSValue = {
// ...
}
}
case class User(id: Int, name: String)
object User {
implicit val read: JSReads[User] = {
case JSObject(Seq(("id",JSNumber(id)), ("name", JSString(name)))) =>
JSSuccess(User(id.toInt, name))
case _ => JSError("Cannot parse to user")
}
}
демо
case class User(id: Int, name: String)
val input =
""" { "id": 5, "name": "Jill"} """
JSON.parse(input).as[User]
Камен Коцев
HackConf 2017