Scala.js:
Когда TypeScript'a уже мало
Бадальянц Юрий, 2017
О себе
- Программирую с 2011 года
- PHP
- JavaScript
- Java
- Scala
TypeScript
≈ JavaScript + Статическая типизация
* и Flow
Динамическая типизация в JS
+ Быстрый старт (низкий порог входа)
+ Код пишется быстро
+ Удобно для прототипов и маленьких проектов
- Сложно поддерживать в большом проекте
- Все ошибки только в рантайме
- Сложно рефакторить и улучшать код
- Легко допустить ошибку
TypeScript
- Добавляется компилятор с проверкой типов
- Код легче поддерживать
- Типы - документация
- Легкие рефакторинги
- Чем больше проект - тем больше профит
Но действительно ли TypeScript избавляет от всех проблем?
Scala
- Богатые ООП и ФП возможности
- Все ES5 фичи
- Все ES6 фичи
- Все ES7 фичи
- Почти все TypeScript фичи
- И многое другое
- Номинальная статическая типизация
- Scala.js - Scala, которая компилируется в JavaScript
var xhr = new XMLHttpRequest()
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
)
xhr.onload = (e) => {
if (xhr.status == 200) {
var r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
JavaScript
Scala.js
var xhr = new XMLHttpRequest()
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
)
xhr.onload = (e: Event) => {
if (xhr.status == 200) {
var r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
JavaScript
Scala.js
var xhr = new XMLHttpRequest()
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
)
xhr.onload = (e: Event) => {
if (xhr.status == 200) {
var r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
var xhr = new XMLHttpRequest()
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
)
xhr.onload = (e) => {
if (xhr.status == 200) {
var r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
function updateUserPassword(
userId: String,
password: String
): Promise<void> {
// implementation
}
updateUserPassword(password, userId)
function updatePasswordForUser(
password: String,
userId: String
): Promise<void> {
// implementation
}
const userId = "16c8bde9-7c3a-400d-aead-bfec582b7103"
const password = "1q2w3e4r5t"
updatePasswordForUser(password, userId)
Ошибка при
рефакторинге
class UserId {
value: String
constructor(str: String) {
this.value = str
}
}
class Password {
value: String
constructor(str: String) {
this.value = str
}
}
const userId = new UserId("16c8bde9-7c3a-400d-aead-bfec582b7103")
const password = new Password("1q2w3e4r5t")
function updatePasswordForUser(
password: Password,
userId: UserId
): Promise<void> {
// implementation
}
function updateUserPassword(
userId: UserId,
password: Password
): Promise<void> {
// implementation
}
updateUserPassword(password, userId)
updateUserPassword(password, userId) // OK!
Structural typing
class UserId {
userId: String constructor(str: String) { this.userId = str } }
class Password { password: String constructor(str: String) { this.password = str } }
Костыль
Boilerplate
Runtime
overhead
updateUserPassword(password, userId)
updateUserPassword(password, userId) // ERROR
class UserId {
value: String constructor(str: String) { this.value = str } }
class Password { value: String constructor(str: String) { this.value = str } }
case class UserId(value: String) extends AnyVal
case class Password(value: String) extends AnyVal
val userId = UserId("16c8bde9-7c3a-400d-aead-bfec582b7103")
val password = Password("1q2w3e4r5t")
def updatePasswordForUser(
password: Password,
userId: UserId
): Future[Unit] = {
// implementation
}
def updateUserPassword(
userId: UserId,
password: Password
): Future[Unit] = {
// implementation
}
updateUserPassword(password, userId)
updateUserPassword(password, userId) // ERROR
const libPromise: Promise<void> = someLibFunc()
function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Promise timed out")), timeout)
})
return Promise.race([promise, timeoutPromise])
}
withTimeout(libPromise, 1000)
.then((value) => {
console.log(value)
})
.catch(e => {
console.error(e)
})
libPromise.withTimeout(1000)
.then((value) => {
console.log(value)
})
.catch(e => {
console.error(e)
})
Prototype
monkey
patching
val libFuture: Future[Unit] = someLibFunc()
implicit class FutureExtensions[T](val self: Future[T]) extends AnyVal {
def withTimeout(timeout: Long): Future[T] = {
val promise = Promise[T]()
setTimeout(() => {
promise.failure(new RuntimeException("Future timed out"))
}, timeout)
Future.firstCompletedOf(Seq(self, promise.future))
}
}
libFuture.withTimeout(1000).onComplete {
case Success(value) => println(value)
case Failure(e) => println(e)
}
val timeout = 3000
getUser(someUserId, timeout)
updateUser(someUser, timeout)
deleteUser(someUserId, timeout)
val timeout = 3000
getUser(someUserId, timeout)
updateUser(someUser, timeout)
deleteUser(someUserId, timeout)
def getUser(userId: UserId, timeout: Long): Future[Option[User]]
def updateUser(user: User, timeout: Long): Future[Unit]
def deleteUser(userId: UserId, timeout: Long): Future[Unit]
def getUser(userId: UserId)(implicit timeout: Long): Future[Option[User]]
def updateUser(user: User)(implicit timeout: Long): Future[Unit]
def deleteUser(userId: UserId)(implicit timeout: Long): Future[Unit]
implicit val timeout: Long = 3000
getUser(someUserId)
updateUser(someUser)
deleteUser(someUserId)
implicit val timeout = 3000
getUser(someUserId)
updateUser(someUser)
deleteUser(someUserId)
libFuture.withTimeout(1000).onComplete {
case Success(value) => println(value)
case Failure(e) => println(e)
}
val sessionTime = 3600
val activeTime = 60
Неочевидно
implicit val timeout = 3000 millis getUser(someUserId) updateUser(someUser) deleteUser(someUserId)
libFuture.withTimeout(1 second).onComplete { case Success(value) => println(value) case Failure(e) => println(e) }
val sessionTime = 1 hour val activeTime = 60 seconds
def seconds = ???
obj.seconds
obj seconds // postfix notation
1.second // implicit class + normal notation
1 second // implicit class + postfix notation
DSL
html(
head(
script(src:="..."),
script(
"alert('Hello World')"
)
),
body(
div(
h1(id:="title", "This is a title"),
p("This is a big paragraph of text")
)
)
)
DSL
'li("class" -> itemState,
'div("class" -> "view",
'input("class" -> "toggle",
"type" -> "checkbox",
"checked" -> props.item.checked,
"onclick" -> toggle _
),
'label("ondblclick" -> editStart _, props.item.title),
'button("class" -> "destroy",
"onclick" -> destroy _
)
)
)
DSL
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="https://github.com/atry">Yang Bo</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
DSL
<table border="1" cellPadding="5">
<tbody>
{
for (contact <- data) yield {
<tr>
<td>
{contact.name.bind}
</td>
<td>
{contact.email.bind}
</td>
</tr>
}
}
</tbody>
</table>
Полезные ссылки
http://www.scala-js.org/ - официальный сайт scala.js
https://gitter.im/scala-js/scala-js - gitter канал scala.js
http://www.lihaoyi.com/hands-on-scala-js/ - электронная книга
https://github.com/ochrons/scalajs-spa-tutorial - SPA приложение
https://telegram.me/scala_ru - русскоязычный telegram канал
Спасибо!
@lmnet89
lmnet
lmnet89@gmail.com
Бадальянц Юрий, 2017
www.linkedin.com/in/lmnet
Scala.js:Когда TypeScript'a уже мало
By Yury Badalyants
Scala.js:Когда TypeScript'a уже мало
- 570