1643172748
Below you can see the same functionality implemented in JavaScript ES6 and Scala.js. See any differences?
ES6
const xhr = new XMLHttpRequest();
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
);
xhr.onload = (e) => {
if (xhr.status === 200) {
const r = JSON.parse(xhr.responseText);
$("#tweets").html(parseTweets(r));
}
};
xhr.send();
Scala.js
val 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) {
val r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
Even though Scala language comes from a very different background than JavaScript, typical Scala code is quite understandable for JavaScript developers. This section will walk you through the differences and show you how to write basic Scala code. If you have fallen in love with the new features in ES6 like arrow functions or destructuring, you can find them all in Scala as well!
The walk-through has been split into three parts:
Each part has a lot of code examples comparing ES6 code to corresponding Scala code.
This is a short introduction to the Scala language for those familiar with JavaScript ES6. We are comparing to ES6 instead of earlier versions of JavaScript because ES6 contains many nice features and syntax changes that bring it closer to Scala.
The best way to experiment with Scala is to use a Scala REPL, or the worksheet functionality in the Scala IDE or IntelliJ IDEA.
Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages. Scala is a pure object-oriented language in the sense that every value is an object. It is also a functional language in the sense that every function is a value and that immutability is favored over mutability.
This combination of paradigms is not alien to JavaScript developers. JavaScript is also object-oriented, although primitive values are not considered as objects. It is at least partially functional as well, since functions are values and can be passed around to other functions (such as Array.prototype.map
). However, although it is possible to write with an immutable mindset in JavaScript, the language does not encourage immutability.
The biggest difference to JavaScript is that Scala is statically typed. This means that it is equipped with an expressive type system that enforces statically that abstractions are used in a safe and coherent manner, meaning the compiler will catch many typical programming errors. If you have used other statically typed languages like Java or C# before you may have noticed that type definitions are all over the place. This is not true with Scala where the compiler can infer most of the types automatically.
Let’s start with something simple, variables. Both Scala and ES6 support mutable and immutable variables.
ES6
// mutable variable
let x = 5;
// immutable variable
const y = "Constant";
Scala
// mutable variable
var x = 5
// immutable variable
val y = "Constant"
Note that the Scala compiler automatically infers the types for x
and y
from the values that are assigned. In Scala both mutable and immutable variables must always be initialized when declared.
Scala defines several primitive types, of which most have corresponding types in JavaScript as well. The following table presents the most commonly used ones (there are also Byte
, Short
and Float
).
Scala type | JavaScript type | Notes |
---|---|---|
String | string | |
Boolean | boolean | |
Int | number | integer, range (-2147483648, 2147483647) |
Double | number | 64-bit floating point, fully equivalent to JS number |
Long | N/A | 64-bit integer |
Char | N/A | UTF-16 code unit |
Unit | undefined | |
Null | null |
In JavaScript all numbers are represented as 64-bit floating point internally, which may give surprising results when making some calculations. In Scala calculations are always performed using the types of operands, so dividing an Int
with another Int
, the result is rounded to an Int
.
ES6
const x = 5 / 3; // == 1.6666666666666667
Scala
val x = 5 / 3 // == 1
val y = 5.0 / 3 // == 1.6666666666666667
val z = 5 / 3.0 // == 1.6666666666666667
Because in JavaScript every number is a number
there is no need to do type conversions. In Scala, however, it is an error if you try to assign a higher precision value to a lower precision variable. You must explicitly convert it using an appropriate function.
Scala
val x: Double = 3 // Ok!
val y: Int = 3.5 // Compile error
val z: Int = 3.5.toInt // Ok!
val a: Int = x // Compile error
val b: Int = x.toInt // Ok!
Defining functions is quite similar in both languages. You just replace the function
keyword with def
, and add types for the parameters and the result type. Speaking of which, this is about the only place you will explicitly write types in Scala. The types of local values inside functions are usually inferred by the compiler. Actually, even the result type will usually be inferred too, but it is good practice to explicitly define it, to help catch potential type errors.
Note that there is no need for a return
keyword: the last expression in the function is automatically used as the return value.
ES6
function mult(x, y) {
return x * y;
}
Scala
def mult(x: Double, y: Double): Double = x * y
In functional programming you quite often need to provide a function as a parameter, but you don’t need it elsewhere so it can be anonymous. Both languages support the nice “fat arrow” notation for defining anonymous functions conveniently.
ES6
const f = (x, y) => x + y;
const p = ["Fox", "jumped", "over", "me"];
const l = p.map(s => s.length)
.reduce((a, b) => a + b, 0); // == 15
Scala
val f = (x: Double, y: Double) => x + y
val p = Array("Fox", "jumped", "over", "me")
val l = p.map(s => s.length)
.foldLeft(0)((a, b) => a + b) // == 15
You can also define default values for parameters if they are not supplied when the function is called. For variable number of parameters, you can access those as a Seq
(see the collections part of this tour for more info). Named parameters work just as you would expect in Scala, whereas in ES6 you need to supply them with the object notation.
ES6
// default value
function mult(x, y = 42.0) {
return x * y;
}
// variable number of parameters
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
const s = sum(5, 4, 3, 2, 1); // == 15
// named parameters
function vec({x = 0, y = 0, z = 0}) {
return new Vec(x, y, z);
}
const v = vec({x: 8, z: 42}); // Vec(8, 0, 42)
Scala
// default value
def mult(x: Double, y: Double = 42.0): Double =
x * y
// variable number of parameters
def sum(args: Double*): Double =
args.foldLeft(0.0)((a, b) => a + b)
val s = sum(5, 4, 3, 2, 1) // == 15
// named parameters (works directly)
def vec(x: Int = 0, y: Int = 0, z: Int = 0): Vec =
new Vec(x, y, z)
val v = vec(8, z = 42) // Vec(8, 0, 42)
Again, the Scala compiler can infer all the required types in the code above, including the parameters for the anonymous function given to the foldLeft
function.
if
, while
, for
, match
control structuresAs you would expect, Scala has the regular if-else
and while
control structures found in most programming languages. The big difference to JavaScript is that if
statements are actually expressions returning a value. In JavaScript you have the special a ? b : c
construct to achieve the same result.
ES6
const res = (name === "") ? 0 : 1;
Scala
val res = if (name == "") 0 else 1
The for
construct in Scala is quite different from the for-loop in JavaScript and also much more powerful. You can use it to iterate over numerical ranges or collections in both languages:
ES6
let x = 0;
for (let i = 0; i < 100; i++)
x += i * i;
const p = ["Fox", "jumped", "over", "me"];
for (let s of p) {
console.log(`Word ${s}`);
}
Scala
var x = 0
for (i <- 0 until 100)
x += i * i
val p = Array("Fox", "jumped", "over", "me")
for (s <- p) {
println(s"Word $s")
}
In case you have nested for-loops, you can easily combine them into one for-comprehension in Scala. Inside the for
you can even filter using if
expressions. In Scala a for-comprehension is just syntactic sugar for a series of flatMap
, map
and withFilter
calls making it very handy when dealing with Scala collections.
ES6
function findPairs(n, sum) {
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
if (i + j == sum)
console.log(`Found pair ${i}, ${j}`);
}
}
};
findPairs(20, 31);
Scala
def findPairs(n: Int, sum: Int): Unit = {
for {
i <- 0 until n
j <- i until n if i + j == sum
} println(s"Found pair $i, $j")
}
findPairs(20, 31)
The code above also serves as an example for string interpolation (in Scala) and template strings (in ES6). Both make it easier to construct strings using variables or function calls. In Scala you don’t need to enclose the variable in {}
if it’s just a simple variable name. For more complex cases you’ll need to use the s"Length = ${data.length}"
syntax.
Finally the match
construct provides pattern matching capabilities in Scala. Pattern matching is a complex topic covered in more detail in the advanced section of this article, so here we just focus on the simple use cases like replacing JavaScript switch
/case
with it.
ES6
const animal = "Dog";
let description;
switch(animal) {
case "Cat":
case "Lion":
case "Tiger":
description = "It's feline!";
break;
case "Dog":
case "Wolf":
description = "It's canine!";
break;
default:
description = "It's something else";
}
console.log(description);
Scala
val animal = "Dog"
val description = animal match {
case "Cat" | "Lion" | "Tiger" =>
"It's feline!"
case "Dog" | "Wolf" =>
"It's canine!"
case _ =>
"It's something else"
}
println(description)
In Scala you can use the |
-operator to match multiple choices and there is no need (nor support) for break
, as cases never fall through like they do in JavaScript. For the default case, use the ubiquitous _
syntax (it has many many more uses in Scala!) As with if
, a match
is an expression returning a value that you can directly assign to a variable.
Being an object-oriented language, Scala naturally supports classes with inheritance. In addition to basic classes Scala also has:
case class
es for conveniently storing dataobject
s for singletonstrait
s for defining interfaces and mixinsLet us look at a simple class hierarchy in both languages.
ES6
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
move(dx, dy) {
this.x += dx;
this.y += dy;
}
draw() {
console.log(`Shape at ${this.x}, ${this.y}`);
}
};
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
draw() {
console.log(`Circle at ${this.x}, ${this.y} with radius ${this.r}`);
}
}
const c = new Circle(5, 5, 42);
const r = c.r; // == 42
Scala
// use var to make coordinates mutable
abstract class Shape(var x: Int, var y: Int) {
def move(dx: Int, dy: Int): Unit = {
x += dx
y += dy
}
def draw(): Unit = {
println(s"Shape at $x, $y")
}
}
// r is immutable but accessible outside the class
class Circle(x: Int, y: Int, val r: Int)
extends Shape(x, y) {
override def draw(): Unit = {
println(s"Circle at $x, $y with radius $r")
}
}
val c = new Circle(5, 5, 42)
val r = c.r // == 42
Note that this
is typically omitted in Scala, since the compiler can tell that x
, y
and r
are properties of the enclosing class (and not local variables).
Case classes are a particular kind of class in Scala which have a lot of compiler-generated goodies. They are particularly suited for immutable data containers whose instances are equal if and only if their fields are pairwise equal. They also automatically receive a sensible toString()
representation, and they can be instantiated without the new
keyword.
JavaScript doesn’t quite have a similar construct, but whenever you would use the regular object notation, consider using a case class instead.
ES6
const person = {first: "James", last: "Bond"};
Scala
case class Person(first: String, last: String)
val person = Person("James", "Bond")
Case classes enforce type safety and prevent constructing invalid objects with missing fields.
The Scala compiler automatically generates a proper equals
method for case classes, making comparing them trivial. In ES6 you would typically go for a library like lodash to avoid writing the complex comparison code yourself.
ES6
const o1 = {a: 1, x: "test"};
const o2 = {a: 1, x: "test"};
if (o1 != o2) {
// this doesn't work as expected
console.log("They are not equal!");
}
if (_.isEqual(o1, o2)) {
console.log("They are equal!");
}
Scala
case class AX(a: Int, x: String)
val o1 = AX(1, "test")
val o2 = AX(1, "test")
if (o1 == o2) {
println("They are equal!")
}
Fields in case classes are public and immutable by default (unless you define them with var
modifier) so you cannot make changes to instances. Instead of modifying the instance you make a copy and modify one or more fields during the copy. Scala provides a suitable copy
function for each case class automatically. In ES6 you can use Object.assign
to achieve the same result.
ES6
const o1 = {a: 1, x: "test"};
// start with empty object to prevent
// modification of o1
const o2 = Object.assign({}, o1, {a: 42});
Scala
case class AX(a: Int, x: String)
val o1 = AX(1, "test")
val o2 = o1.copy(a = 42)
Finally case classes can be used nicely in pattern matching, which is covered in the advanced section.
An object
is a special class with only a single instance: a singleton. JavaScript also has a singleton design pattern (or actually several) even though the language itself does not have direct support for the concept. Singletons are useful for putting stuff in a shared namespace without polluting the global scope.
ES6
const RandomGen = {
_privateMethod() {
console.log("I am private");
},
_rnd() {
return Math.random()
},
publicMethod() {
console.log("The public can see me!");
this._privateMethod();
},
name: "RandomGen",
getRandomNumber() {
return this._rnd()
}
}
const r = RandomGen.getRandomNumber();
Scala
import scala.util.Random
object RandomGen {
private def privateMethod(): Unit = {
println("I am private")
}
private val rnd = new Random()
def publicMethod(): Unit = {
println("The public can see me!")
privateMethod()
}
val name = "RandomGen"
def getRandomNumber: Double = rnd.nextDouble()
}
val r = RandomGen.getRandomNumber
As you can see, defining singleton objects in Scala is quite trivial thanks to the native support in the language.
Another common use for object
s in Scala is using them as companion objects for classes to store static variables and methods shared by all instances of the class.
Scala trait
s are similar to the mixin design pattern in JavaScript by allowing developer to define behaviors for class composition. Because of Scala’s strict type system, traits are commonly used to describe common interfaces for a group of implementation classes. JavaScript itself has no need for interfaces, but some extensions like TypeScript support them for the same purpose as Scala.
ES6
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
draw() {
console.log(`Circle at ${this.x}, ${this.y} with radius ${this.r}`);
}
}
const Clickable = {
onClick() {
console.log("Clicked!");
}
};
class ClickableCircle extends Circle {}
Object.assign(ClickableCircle.prototype, Clickable);
const cc = new ClickableCircle(0, 0, 42);
cc.onClick();
Scala
class Circle(x: Int, y: Int, val r: Int)
extends Shape(x, y) {
override def draw(): Unit = {
println(s"Circle at $x, $y with radius $r")
}
}
trait Clickable {
def onClick(): Unit = {
println("Clicked!")
}
}
class ClickableCircle(x: Int, y: Int, r: Int)
extends Circle(x, y, r) with Clickable
val cc = new ClickableCircle(0, 0, 42)
cc.onClick()
Note that there are many ways for defining mixins in JavaScript, using Object.assign
is just one of them supported by ES6.
Option
, the type safe undefined
The notorious undefined
type in JavaScript can be a blessing or a curse. On the other hand it makes life easy by allowing you to drop function parameters or leave variables undefined. But then it also masks many errors and makes you write extra code to check for undefined
. Quite often undefined
is used to make a distinction between an existing value (of any type) and a missing value.
Scala doesn’t have undefined
(it does have null
but its use is discouraged), but instead it has an Option
trait for representing optional values. In Scala.js the undefined
type exists to support interoperability with JS libraries, but even there it is recommended to use Option
whenever possible.
Option[A]
is a container for an optional value of type A
(note that Option[A]
is Scala’s notation for type parameters, which most programming languages, including TypeScript, write as Option<A>
). If the value of type A
is present, the Option[A]
is an instance of Some[A]
, containing the present value of type A
. If the value is absent, the Option[A]
is the object None
.
ES6
function log(msg, context) {
let s;
if (context !== undefined)
s = `[${context}] ${msg}`;
else
s = msg;
console.log(s);
};
// produces: First message
log("First message");
// produces: [debug] Second message
log("Second message", "debug");
Scala
def log(msg: String,
context: Option[String] = None): Unit = {
val s = context match {
case Some(c) => s"[$c] $msg"
case None => msg
}
println(s)
}
log("First message")
log("Second message", Some("debug"))
Pattern matching works nicely with Option
, but there are more powerful ways to use it. Let’s rewrite the previous function another way giving us the same result.
Scala
def log(msg: String, context: Option[String] = None): Unit = {
val s = context.map(c => s"[$c] $msg").getOrElse(msg)
println(s)
}
Whoa, quite a reduction in code size! Next let’s see how we can process a sequence of option values.
ES6
const data = [1, 2, 3, undefined, 5, undefined, 7];
const res = data.filter((x) => x !== undefined);
Scala
val data = Array(Some(1), Some(2), Some(3),
None, Some(5), None, Some(7))
val res = data.filter(x => x.isDefined)
Option
provides many collection like methods like map
, filter
and flatMap
In JavaScript there are basically two kinds of collections you have used to store your data: the Array
for sequential data and Object
(aka dictionary or hash map) for storing key-value pairs. Furthermore both of these are mutable by default, so if you pass them to a function, that function might go and modify them without your knowledge.
ES6 extends your options with four new collection types Map
, Set
, WeakMap
and WeakSet
. Of these the WeakMap
and WeakSet
are for special purposes only, so in your application you would typically use only Map
and Set
.
Unlike JavaScript, the Scala standard library has a huge variety of different collection types to choose from. Furthermore the collections are organized in a type hierarchy, meaning they share a lot of common functionality and interfaces. The high-level hierarchy for the abstract base classes and traits is shown in the image below.
Scala provides immutable and mutable implementations for all these collection types.
Common immutable collections | |
---|---|
Seq | List , Vector , Stream , Range |
Map | HashMap , TreeMap |
Set | HashSet , TreeSet |
Common mutable collections | |
Seq | Buffer , ListBuffer , Queue , Stack |
Map | HashMap , LinkedHashMap |
Set | HashSet |
Let’s start with familiar things and see how Scala collections compare with the JavaScript Array
and Object
(or Map
). The closest match for Array
would be the mutable Buffer
since arrays in Scala cannot change size after initialization. For Object
(or Map
) the best match is the mutable HashMap
.
A simple example of array manipulation.
ES6
const a = ["Fox", "jumped", "over"];
a.push("me"); // Fox jumped over me
a.unshift("Red"); // Red Fox jumped over me
const fox = a[1];
a[a.length - 1] = "you"; // Red Fox jumped over you
console.log(a.join(" "));
Scala
import scala.collection.mutable
val a = mutable.Buffer("Fox", "jumped", "over")
a.append("me") // Fox jumped over me
a.prepend("Red") // Red Fox jumped over me
val fox = a(1)
a(a.length - 1) = "you" // Red Fox jumped over you
println(a.mkString(" "))
Working with a hash map (or Object).
ES6
const p = {first: "James", last: "Bond"};
p["profession"] = "Spy";
const name = `${p.first} ${p.last}`
Scala
import scala.collection.mutable
val p = mutable.HashMap("first" -> "James",
"last" -> "Bond")
p("profession") = "Spy"
val name = s"${p("first")} ${p("last")}"
Even though you can use Scala collections like you would use arrays and objects in JavaScript, you really shouldn’t, because you are missing a lot of great functionality.
Seq
, Map
, Set
and Tuple
For 99% of the time you will be working with those four common collection types in your code. You will instantiate implementation collections like Vector
or HashMap
, but in your code you don’t really care what the implementation is, as long as it behaves like a Seq
or a Map
.
You may have noticed that Tuple
is not shown in the collection hierarchy above, because it’s a very specific collection type of its own. Scala tuple combines a fixed number of items together so that they can be passed around as a whole. A tuple is immutable and can hold different types, so it’s quite close to an anonymous case class in that sense. Tuples are used in situations where you need to group items together, like key and value in a map, or to return multiple values. In JavaScript you can use a fixed size array to represent a tuple.
ES6
const t = ["James", "Bond", 42];
const kv = ["key", 42];
function sumProduct(s) {
let sum = 0;
let product = 1;
for(let i of s) {
sum += i;
product *= i;
}
return [sum, product];
}
Scala
val t = ("James", "Bond", 42)
val kv = "key" -> 42 // same as ("key", 42)
def sumProduct(s: Seq[Int]): (Int, Int) = {
var sum = 0
var product = 1
for(i <- s) {
sum += i
product *= i
}
(sum, product)
}
To access values inside a tuple, use the tuple._1
syntax, where the number indicates position within the tuple (starting from 1, not 0). Quite often you can also use destructuring to extract the values.
ES6
const sc = sumProduct([1, 2, 3]);
const sum = sc[0];
const product = sc[1];
// with destructuring
const [sum, product] = sumProduct([1, 2, 3]);
Scala
val sc = sumProduct(Seq(1, 2, 3))
val sum = sc._1
val product = sc._2
// with destructuring
val (sum, product) = sumProduct(Seq(1, 2, 3))
Seq
is an ordered sequence. Typical implementations include List
, Vector
, Buffer
and Range
. Although Scala Array
is not a Seq
, it can be wrapped into a WrappedArray
to enable all Seq
operations on arrays. In Scala this is done automatically through an implicit conversion, allowing you to write code like following.
Scala
val ar = Array(1, 2, 3, 4)
val product = ar.foldLeft(1)((a, x) => a * x) // foldLeft comes from WrappedArray
The Seq
trait exposes many methods familiar to the users of JavaScript arrays, including foreach
, map
, filter
, slice
and reverse
. In addition to these, there are several more useful methods shown with examples in the code block below.
Scala
val seq = Seq(1, 2, 3, 4, 5)
seq.isEmpty == false
seq.contains(6) == false // JS Array.includes()
seq.forall(x => x > 0) == true // JS Array.every()
seq.exists(x => x % 3 == 0) == true // JS Array.some()
seq.find(x => x > 3) == Some(4) // JS Array.find()
seq.head == 1
seq.tail == Seq(2, 3, 4, 5)
seq.last == 5
seq.init == Seq(1, 2, 3, 4)
seq.drop(2) == Seq(3, 4, 5) // JS Array.slice()
seq.dropRight(2) == Seq(1, 2, 3)
seq.count(x => x < 3) == 2
seq.groupBy(x => x % 2) == Map(1 -> Seq(1, 3, 5), 0 -> Seq(2, 4))
seq.sortBy(x => -x) == Seq(5, 4, 3, 2, 1)
seq.partition(x => x > 3) == (Seq(4, 5), Seq(1, 2, 3))
seq :+ 6 == Seq(1, 2, 3, 4, 5, 6)
seq ++ Seq(6, 7) == Seq(1, 2, 3, 4, 5, 6, 7) // JS Array.concat()
The functionality offered by Array.reduce
in JavaScript is covered by two distinct methods in Scala: reduceLeft
and foldLeft
. The difference is that in foldLeft
you provide an initial (“zero”) value (which is an optional parameter to Array.reduce
) while in reduceLeft
you don’t. Also note that in foldLeft
, the type of the accumulator can be something else, for example a tuple, but in reduceLeft
it must always be a supertype of the value. Since reduceLeft
cannot deal with an empty collection, it is rarely useful.
ES6
function sumProduct(s) {
// destructuring works in the function argument
return s.reduce(([sum, product], x) =>
[sum + x, product * x],
[0, 1] // use an array to represent a tuple
);
}
Scala
def sumProduct(s: Seq[Int]): (Int, Int) = {
// use a tuple accumulator to hold sum and product
s.foldLeft((0, 1)) { case ((sum, product), x) =>
(sum + x, product * x)
}
}
A Map
consists of pairs of keys and values. Both keys and values can be of any valid Scala type, unlike in JavaScript where an Object
may only contain string
or symbol
keys (the new ES6 Map
allows using other types as keys, but supports only referential equality for comparing keys).
JavaScript Object
doesn’t really have methods for using it as a map, although you can iterate over the keys with Object.keys
. When using Object
as a map, most developers use utility libraries like lodash to get access to suitable functionality. The ES6 Map
object contains keys
, values
and forEach
methods for accessing its contents, but all transformation methods are missing.
You can build a map directly or from a sequence of key-value pairs.
ES6
// object style map
const m = {first: "James", last: "Bond"};
// ES6 Map
const data = [["first", "James"], ["last", "Bond"]];
const m2 = new Map(data);
Scala
val m = Map("first" -> "James", "last" -> "Bond")
val data = Seq("first" -> "James", "last" -> "Bond")
val m2 = Map(data:_*)
In Scala when a function expects a variable number of parameters (like the Map
constructor), you can destructure a sequence with the seq:_*
syntax, which is the equivalent of ES6’s spread operator ...seq
.
Accessing Map
contents can be done in many ways.
ES6
// object syntax
const name = `${m.last}, ${m.first} ${m.last}`
// ES6 Map syntax
const name2 = `${m2.get("last")}, ${m2.get("first")} ${m2.get("last")}`
// use default value when missing
const age = m.age === undefined ? "42" : m.age;
// check all fields are present
const person = m.first !== undefined &&
m.last !== undefined &&
m.age !== undefined ? `${m.last}, ${m.first}: ${m.age}` :
"missing";
Scala
val name = s"${m("last")}, ${m("first")} ${m("last")}"
// use default value when missing
val age = m.getOrElse("age", "42")
// check all fields are present
val person = (for {
first <- m.get("first")
last <- m.get("last")
age <- m.get("age")
} yield {
s"$last, $first: $age"
}).getOrElse("missing")
In the previous example m.get("first")
returns an Option[String]
indicating whether the key is present in the map or not. By using a for comprehension, we can easily extract three separate values from the map and use them to build the result. The result from for {} yield
is also an Option[String]
so we can use getOrElse
to provide a default value.
Let’s try something more complicated. Say we need to maintain a collection of players and all their game scores. This could be represented by a Map[String, Seq[Int]]
ES6
const scores = {};
function addScore(player, score) {
if (scores[player] === undefined)
scores[player] = [];
scores[player].push(score);
}
function bestScore() {
let bestScore = 0;
let bestPlayer = "";
for (let player in scores) {
const max = scores[player].reduce((a, score) =>
Math.max(score, a)
);
if (max > bestScore) {
bestScore = max;
bestPlayer = player;
}
}
return [bestPlayer, bestScore];
}
function averageScore() {
let sum = 0;
let count = 0;
for (let player in scores) {
for (let score of scores[player]) {
sum += score;
count++;
}
}
if (count == 0)
return 0;
else
return Math.round(sum / count);
}
Scala
import scala.collection.mutable
val scores =
mutable.Map.empty[String, mutable.Buffer[Int]]
def addScore(player: String, score: Int): Unit = {
scores.getOrElseUpdate(player, mutable.Buffer())
.append(score)
}
def bestScore: (String, Int) = {
val all = scores.toList.flatMap {
case (player, pScores) =>
pScores.map(s => (player, s))
}
if (all.isEmpty)
("", 0)
else
all.maxBy(_._2)
}
def averageScore: Int = {
val allScores = scores.flatMap(_._2)
if (allScores.isEmpty)
0
else
allScores.sum / allScores.size
}
In the example above the both versions are using mutable collections. Coming from JavaScript it’s good to start with the more familiar mutable collections, but over time Scala developers tend to favor immutable versions. Immutable collections in Scala use structural sharing to minimize copying and to provide good performance. Sharing is ok, because the data is immutable!
The best score is found by first flattening the whole structure into a sequence of (player, score) pairs. Then we use the maxBy
method to find the maximum score by looking at the second value in the tuple.
The average is calculated simply by flattening all scores into a single sequence and then calculating its average.
A Set
is like a Map
without values, just the distinct keys. In JavaScript it’s typical to emulate a Set by storing the values as keys into an Object
. This of course means that the values must be converted to strings. In ES6 there is a new Set
type that works with all kinds of value types, but like with Map
, it’s based on reference equality, making it less useful when dealing with complex value types.
As their name implies, sets have no duplicate elements. Adding values to a set automatically guarantees that all duplicate values are eliminated.
Set operations like diff
, intersect
and union
allow you to build new sets out of other sets to check, for example, what has changed.
Scala
val set1 = Set(1, 2, 3, 4, 5)
val set2 = Set(2, 3, 5, 1, 6)
val addedValues = set2 diff set1 // Set(6)
val removedValues = set1 diff set2 // Set(4)
Note how in Scala you can also omit the .
and parentheses in method calls.
Sets are also a convenient way to check for multiple values in methods like filter
.
ES6
const common = {"a": true, "the": true,
"an": true, "and": true};
const text = "The sun is a star and an energy source"
const words = text.split(" ")
.map(s => s.toLowerCase())
.filter(s => !common[s]);
Scala
val common = Set("a", "the", "an", "and")
val text = "The sun is a star and an energy source"
val words = text.split(" ")
.map(_.toLowerCase)
.filterNot(common)
// Array(sun, is, star, energy, source)
Scala is a feature rich language that is easy to learn but takes time to master. Depending on your programming background, typically you start by writing Scala as you would’ve written the language you know best (JavaScript, Java or C# for example) and gradually learn more and more idiomatic Scala paradigms to use. In this section we cover some of the more useful design patterns and features, to get you started quickly.
In the Basics part we already saw simple examples of pattern matching as a replacement for JavaScript’s switch
statement. However, it can be used for much more, for example checking the type of input.
ES6
function printType(o) {
switch (typeof o) {
case "string":
console.log(`It's a string: ${o}`);
break;
case "number":
console.log(`It's a number: ${o}`);
break;
case "boolean":
console.log(`It's a boolean: ${o}`);
break;
default:
console.log(`It's something else`);
}
}
Scala
def printType(o: Any): Unit = {
o match {
case s: String =>
println(s"It's a string: $s")
case i: Int =>
println(s"It's an int: $i")
case b: Boolean =>
println(s"It's a boolean: $b")
case _ =>
println("It's something else")
}
Pattern matching uses something called partial functions which means it can be used in place of regular functions, for example in a call to filter
or map
. You can also add a guard clause in the form of an if
, to limit the match. If you need to match to a variable, use backticks to indicate that.
ES6
function parse(str, magicKey) {
let res = [];
for(let c of str) {
if (c === magicKey)
res.push("magic");
else if (c.match(/\d/))
res.push("digit");
else if (c.match(/\w/))
res.push("letter");
else if (c.match(/\s/))
res.push(" ");
else
res.push("char");
}
return res;
}
const r = parse("JB/007", '/');
// [letter, letter, magic, digit, digit, digit]
Scala
def parse(str: String, magicKey: Char): Seq[String] = {
str.map {
case c if c == magicKey =>
"magic"
case c if c.isDigit =>
"digit"
case c if c.isLetter =>
"letter"
case c if c.isWhitespace =>
" "
case c =>
"char"
}
}
val r = parse("JB/007", '/')
// Seq(letter, letter, magic, digit, digit, digit)
Where pattern matching really shines is at destructuring. This means matching to a more complex pattern and extracting values inside that structure. ES6 also supports destructuring (yay!) in assignments and function parameters, but not in matching.
ES6
const person = {first: "James", last: "Bond", age: 42};
const {first, last, age: years} = person;
// first = "James", last = "Bond", years = 42
const seq = [1, 2, 3, 4, 5];
const [a, b, , ...c] = seq;
// a = 1, b = 2, c = [4, 5]
const seq2 = [a, b].concat(c); // [1, 2, 4, 5]
Scala
case class Person(first: String, last: String, age: Int)
val person = Person("James", "Bond", 42)
val Person(first, last, years) = person
// first = "James", last = "Bond", years = 42
val seq = Seq(1, 2, 3, 4, 5)
val Seq(a, b, _, c @ _*) = seq
// a = 1, b = 2, c = Seq(4, 5)
val seq2 = Seq(a, b) ++ c // Seq(1, 2, 4, 5)
In Scala the destructuring and rebuilding have nice symmetry making it easy to remember how to do it. Use _
to skip values in destructuring.
In pattern matching the use of destructuring results in clean, simple and understandable code.
ES6
function ageSum(persons, family) {
return persons.filter(p => p.last === family)
.reduce((a, p) => a + p.age, 0);
}
const persons = [
{first: "James", last: "Bond", age: 42},
{first: "Hillary", last: "Bond", age: 35},
{first: "James", last: "Smith", age: 55}
];
ageSum(persons, "Bond") == 77;
Scala
def ageSum(persons: Seq[Person],
family: String): Int = {
persons.collect {
case Person(_, last, age) if last == family =>
age
}.sum
}
val persons = Seq(
Person("James", "Bond", 42),
Person("Hillary", "Bond", 35),
Person("James", "Smith", 55)
)
ageSum(persons, "Bond") == 77
We could’ve implemented the Scala function using a filter
and foldLeft
, but it is more understandable using collect
and pattern matching. It would be read as “Collect every person with a last name equaling family
and extract the age of those persons. Then sum up the ages.”
Another good use case for pattern matching is regular expressions (also in ES6!). Let’s extract a date in different formats.
ES6
function convertToDate(d) {
const YMD = /(\d{4})-(\d{1,2})-(\d{1,2})/
const MDY = /(\d{1,2})\/(\d{1,2})\/(\d{4})/
const DMY = /(\d{1,2})\.(\d{1,2})\.(\d{4})/
const [, year, month, day] = YMD.exec(d) || [];
if (year !== undefined) {
return {
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
};
} else {
const [, month, day, year] = MDY.exec(d) || [];
if (year !== undefined) {
return {
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
};
} else {
const [, day, month, year] = DMY.exec(d) || [];
if (year !== undefined) {
return {
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
};
}
}
}
throw new Error("Invalid date!");
}
convertToDate("2015-10-9"); //{year:2015,month:10,day:9}
convertToDate("10/9/2015"); //{year:2015,month:10,day:9}
convertToDate("9.10.2015"); //{year:2015,month:10,day:9}
convertToDate("10 Nov 2015"); // exception
Scala
case class Date(year: Int, month: Int, day: Int)
def convertToDate(d: String): Date = {
val YMD = """(\d{4})-(\d{1,2})-(\d{1,2})""".r
val MDY = """(\d{1,2})/(\d{1,2})/(\d{4})""".r
val DMY = """(\d{1,2})\.(\d{1,2})\.(\d{4})""".r
d match {
case YMD(year, month, day) =>
Date(year.toInt, month.toInt, day.toInt)
case MDY(month, day, year) =>
Date(year.toInt, month.toInt, day.toInt)
case DMY(day, month, year) =>
Date(year.toInt, month.toInt, day.toInt)
case _ =>
throw new Exception("Invalid date!")
}
}
convertToDate("2015-10-9") // = Date(2015,10,9)
convertToDate("10/9/2015") // = Date(2015,10,9)
convertToDate("9.10.2015") // = Date(2015,10,9)
convertToDate("10 Nov 2015") // exception
Here we use triple-quoted strings that allow us to write regex without escaping special characters. The string is converted into a Regex
object with the .r
method. Because regexes extract strings, we need to convert matched groups to integers ourselves.
We covered the basic use functions in Part 1, but Scala, being a functional programming language, provides much more when it comes to functions. Let’s explore some of the more advanced features and how they compare to JavaScript.
Scala, as JavaScript, allows the definition of higher-order functions. These are functions that take other functions as parameters, or whose result is a function. Higher-order functions should be familiar to JavaScript developers, because they often appear in form of functions that take callbacks as parameters.
Typically higher-order functions are used to pass specific functionality to a general function, like in the case of Array.prototype.filter
in ES6 or Seq.filter
in Scala. We can use this to build a function to calculate a minimum and maximum from a sequence of values, using a function to extract the target value.
ES6
function minmaxBy(arr, f) {
return arr.reduce(
([min, max], e) => {
const v = f(e);
return [Math.min(min, v), Math.max(max, v)]
},
[Number.MAX_VALUE, Number.MIN_VALUE]
)
}
const [youngest, oldest] = minmaxBy(persons, e => e.age);
Scala
def minmaxBy[T](seq: Seq[T], f: T => Int): (Int, Int) = {
seq.foldLeft((Int.MaxValue, Int.MinValue)) {
case ((min, max), e) =>
val v = f(e)
(math.min(min, v), math.max(max, v))
}
}
val (youngest, oldest) = minmaxBy[Person](persons, _.age)
In some cases you want to defer the evaluation of a parameter value until when it’s actually used in the function. For this purpose Scala offers call-by-name parameters. This can be useful when dealing with an expensive computation that is only optionally used by the function. In JavaScript the closest thing to this is to wrap a value in an anonymous function with no arguments and pass that as a parameter, but that’s more verbose and error-prone. You need to remember to both wrap the value and call the function.
ES6
function compute(value, cPos, cNeg) {
if (value >= 0)
return cPos();
else
return cNeg();
}
compute(x, () => expCalc(), () => expCalc2());
Scala
def compute(value: Int, cPos: => Int, cNeg: => Int) = {
if (value >= 0)
cPos
else
cNeg
}
compute(x, expCalc, expCalc2)
Recursive functions can be very expressive, but they may also cause spurious stack overflows if the recursion gets too deep. Scala automatically optimizes recursive functions that are tail recursive, allowing you to use them without fear of overflowing the stack. To make sure your function is actually tail recursive, use the @tailrec
annotation, which will cause the Scala compiler to report an error if your function is not tail recursive.
Before ES6, JavaScript did not support tail call optimization, nor optimizing tail recursive functions. If you use a smart ES6 transpiler, it can actually convert a tail recursive function into a while
loop, but there are no checks available to help you to verify the validity of tail recursion.
ES6
function fib(n) {
function fibIter(n, next, prev) {
if (n === 0) {
return prev;
} else {
return fibIter(n - 1, next + prev, next);
}
};
return fibIter(n, 1, 0);
}
Scala
def fib(n: Int): Int = {
@tailrec
def fibIter(n: Int, next: Int, prev: Int): Int = {
if (n == 0)
prev
else
fibIter(n - 1, next + prev, next)
}
fibIter(n, 1, 0)
}
In Scala you can call a function with only some of its arguments and get back a function taking those missing arguments. You do this by using _
in place of the actual parameter. In JavaScript you can achieve the same by using the Function.prototype.bind
function (although it limits you to providing parameters from left to right). For example we can define a function to create HTML tags by wrapping content within start and end tags.
ES6
function tag(name, content) {
return `<${name}>${content}</${name}>`
}
const div = tag.bind(null, "div");
const p = tag.bind(null, "p");
const html = div(p("test")); // <div><p>test</p></div>
Scala
def tag(name: String, content: String) = {
s"<$name>$content</$name>"
}
val div = tag("div", _: String)
val p = tag("p", _: String)
val html = div(p("test")) // <div><p>test</p></div>
Scala allows a function to be defined with multiple parameter lists. In Scala this is quite common as it provides some powerful secondary benefits besides the usual currying functionality. JavaScript does not directly support multiple parameter lists in its syntax, but you can emulate it by returning a chain of functions, or by using libraries like lodash that do it for you.
Let’s use currying to define the tag
function from previous example.
ES6
function tag(name) {
return (content) => `<${name}>${content}</${name}>`;
}
const div = tag("div");
const p = tag("p");
const html = div(p("test")); // <div><p>test</p></div>
Scala
def tag(name: String)(content: String): String = {
s"<$name>$content</$name>"
}
val div = tag("div") _
val p = tag("p") _
val html = div(p("test")) // <div><p>test</p></div>
Multiple parameter lists also helps with type inference, meaning we don’t need to tell the compiler the types explicitly. For example we can rewrite the minmaxBy
function as curried, which allows us to leave the Person
type out when calling it, as it is automatically inferred from the first parameter. This is why methods like foldLeft
are defined with multiple parameter lists.
Scala
def minmaxBy[T](seq: Seq[T])(f: T => Int): (Int, Int) = {
seq.foldLeft((Int.MaxValue, Int.MinValue)) {
case ((min, max), e) =>
val v = f(e)
(math.min(min, v), math.max(max, v))
}
}
val (youngest, oldest) = minmaxBy(persons)(_.age)
Being type safe is great in Scala, but sometimes the type system can be a bit prohibitive when you want to do something else, like add methods to existing classes. To allow you to do this in a type safe manner, Scala provides implicits. You can think of implicits as something that’s available in the scope when you need it, and the compiler can automatically provide it. For example we can provide a function to automatically convert a JavaScript Date
into a Scala/Java Date
.
Scala
import scalajs.js
implicit def convertFromJSDate(d: js.Date): java.util.Date = {
new java.util.Date(d.getMilliseconds())
}
implicit def convertToJSDate(d: java.util.Date): js.Date = {
new js.Date(d.getTime)
}
case class Person(name: String, joined: js.Date)
val p = Person("James Bond", new java.util.Date)
When these implicit conversion functions are in lexical scope, you can use JS and Scala dates interchangeably. Outside the scope they are not visible and you must use correct types or explicitly convert between each other.
The monkey patching term became famous among Ruby developers and it has been adopted into JavaScript to describe a way of extending existing classes with new methods. It has several pitfalls in dynamic languages and is generally not a recommended practice. Especially dangerous is to patch JavaScript’s host objects like String
or DOM.Node
. This technique is, however, commonly used to provide support for new JavaScript functionality missing from older JS engines. The practice is known as polyfilling or shimming.
In Scala providing extension methods via implicits is perfectly safe and even a recommended practice. The Scala standard library does it all the time. For example did you notice the .r
or .toInt
functions that were used on strings in the regex example? Both are extension methods coming from implicit classes.
Let’s use the convertToDate
we defined before and add a toDate
extension method to String
by defining an implicit class.
ES6
String.prototype.toDate = function() {
return convertToDate(this);
}
"2015-10-09".toDate(); // = {year:2015,month:10,day:9}
Scala
implicit class StrToDate(val s: String) {
def toDate = convertToDate(s)
}
"2015-10-09".toDate // = Date(2015,10,9)
Note that the JavaScript version modifies the global String
class (dangerous!), whereas the Scala version only introduces a conversion from String
to a custom StrToDate
class providing an additional method. Implicit classes are safe because they are lexically scoped, meaning the StrToDate
is not available in other parts of the program unless explicitly imported. The toDate
method is not added to the String
class in any way, instead the compiler generates appropriate code to call it when required. Basically "2010-10-09".toDate
is converted into new StrToDate("2010-10-09").toDate
.
Scala IDEs are also smart enough to know what implicit extension methods are in scope and will show them to you next to the other methods.
Implicit extension methods are safe and easy to refactor. If you, say, rename or remove a method, the compiler will immediately give errors in places where you use that method. IDEs provide great tools for automatically renaming all instances when you make the change, keeping your code base operational. You can even do complex changes like add new method parameters or reorder them and the IDE can take care of the refactoring for you, safely and automatically, thanks to strict typing.
Finally we’ll make DOM’s NodeList
behave like a regular Scala collection to make it easier to work with them. Or to be more accurate, we are extending DOMList[T]
which provides a type for the nodes. NodeList
is actually just a DOMList[Node]
.
Scala
implicit class NodeListSeq[T <: Node](nodes: DOMList[T]) extends IndexedSeq[T] {
override def foreach[U](f: T => U): Unit = {
for (i <- 0 until nodes.length) {
f(nodes(i))
}
}
override def length: Int = nodes.length
override def apply(idx: Int): T = nodes(idx)
}
Defining just those three functions, we now have access to all the usual collection functionality like map
, filter
, find
, slice
, foldLeft
, etc. This makes working with NodeList
s a lot easier and safer. The implicit class makes use of Scala generics, providing implementation for all types that extend Node
. Note that NodeListSeq
is available as PimpedNodeList
in the scala-js-dom
library; just import org.scalajs.dom.ext._
to use it.
Scala
// cast to correct element type
val images = dom.document.querySelectorAll("img").asInstanceOf[NodeListOf[HTMLImageElement]]
// get all image source URLs
val urls = images.map(i => i.src)
// filter images that have "class" attribute set
val withClass = images.filter(i => i.className.nonEmpty)
// set an event listener to 10 widest images
images.sortBy(i => -i.width).take(10).foreach { i =>
i.onclick = (e: MouseEvent) => println("Image clicked!")
}
Writing asynchronous JavaScript code used to be painful due to the number of callbacks required to handle chained asynchronous calls. This is affectionately known as callback hell. Then came the various Promise
libraries that alleviated this issue a lot, but were not fully compatible with each other. ES6 standardizes the Promise
interface so that all implementations (ES6’s own included) can happily coexist.
In Scala a similar concept is the Future
. On the JVM, futures can be used for both parallel and asynchronous processing, but under Scala.js only the latter is possible. Like a JavaScript Promise
, a Future
is a placeholder object for a value that may not yet exist. Both Promise
and Future
can complete successfully, providing a value, or fail with an error/exception. Let’s look at a typical use case of fetching data from server using AJAX.
ES6
// using jQuery
$.ajax("http://api.openweathermap.org/" +
"data/2.5/weather?q=Tampere").then(
(data, textStatus, jqXHR) =>
console.log(data)
);
Scala
import org.scalajs.dom
import dom.ext.Ajax
Ajax.get("http://api.openweathermap.org/" +
"data/2.5/weather?q=Tampere").foreach {
xhr =>
println(xhr.responseText)
}
The JavaScript code above is using jQuery to provide similar helper for making Ajax calls returning promises as is available in the Scala.js DOM library.
Here is a comparison between Scala’s Future
and JavaScript’s Promise
for the most commonly used methods.
Future | Promise | Notes |
---|---|---|
foreach(func) | then(func) | Executes func for its side-effects when the future completes. |
map(func) | then(func) | The result of func is wrapped in a new future. |
flatMap(func) | then(func) | func must return a future. |
recover(func) | catch(func) | Handles an error. The result of func is wrapped in a new future. |
recoverWith(func) | catch(func) | Handles an error. func must return a future. |
filter(predicate) | N/A | Creates a new future by filtering the value of the current future with a predicate. |
zip(that) | N/A | Zips the values of this and that future, and creates a new future holding the tuple of their results. |
Future.successful(value) | Promise.resolve(value) | Returns a successful future containing value |
Future.failed(exception) | Promise.reject(value) | Returns a failed future containing exception |
Future.sequence(iterable) | Promise.all(iterable) | Returns a future that completes when all of the futures in the iterable argument have been completed. |
Future.firstCompletedOf(iterable) | Promise.race(iterable) | Returns a future that completes as soon as one of the futures in the iterable completes. |
Note that Scala has different functions corresponding to JavaScript’s then
, mainly map
and flatMap
. then
is not type-safe, because it will flatten promises “all the way down”, even if that was not your intention. In contrast, map
never flattens, and flatMap
always flattens once, tracking the appropriate static result type.
foreach
is a slight variation of map
that does not return a new future. It is typically used instead of map
to communicate the intent that the callback is executed for its side-effects rather than its result value.
Even though ES6 brought the standard promise API to browsers, all asynchronous functions still require the use of callbacks. To convert a callback into a Future
in Scala you need to use a Promise
. Wait, what? Yes, in addition to Future
, Scala also has a Promise
class which actually implements the Future
trait.
As an example, let’s convert the onload
event of an img
tag into a Future
.
ES6
function onLoadPromise(img) {
if (img.complete) {
return Promise.resolve(img.src);
} else {
const p = new Promise((success) => {
img.onload = (e) => {
success(img.src);
};
});
return p;
}
}
const img = document.querySelector("#mapimage");
onLoadPromise(img).then(url =>
console.log(`Image ${url} loaded`)
);
Scala
def onLoadFuture(img: HTMLImageElement) = {
if (img.complete) {
Future.successful(img.src)
} else {
val p = Promise[String]()
img.onload = { (e: Event) =>
p.success(img.src)
}
p.future
}
}
val img = dom.document.querySelector("#mapimage")
.asInstanceOf[HTMLImageElement]
onLoadFuture(img).foreach { url =>
println(s"Image $url loaded")
}
Because the image might have already loaded when we create the promise, we must check for that separately and just return a completed future in that case.
Next we’ll add an onloadF
extension method to the HTMLImageElement
class, to make it really easy to use the futurized version.
Scala
implicit class HTMLImageElementOps(val img: HTMLImageElement) extends AnyVal {
def onloadF = onLoadFuture(img)
}
val img = dom.document.querySelector("#mapimage").asInstanceOf[HTMLImageElement]
img.onloadF.foreach { url =>
println(s"Image $url loaded")
}
While we are playing with DOM images, let’s create a future that completes once all the images on the page have finished loading. Here we’ll take advantage of the NodeListSeq
extension class to provide us with the map
method on the NodeList
returned from querySelectorAll
.
Scala
val images = dom.document.querySelectorAll("img").asInstanceOf[NodeListOf[HTMLImageElement]]
val loaders = images.map(i => i.onloadF)
Future.sequence(loaders).foreach { urls =>
println(s"All ${urls.size} images loaded!")
}
Original article source at https://www.scala-js.org
#javascript #scala
1626321063
PixelCrayons: Our JavaScript web development service offers you a feature-packed & dynamic web application that effectively caters to your business challenges and provide you the best RoI. Our JavaScript web development company works on all major frameworks & libraries like Angular, React, Nodejs, Vue.js, to name a few.
With 15+ years of domain expertise, we have successfully delivered 13800+ projects and have successfully garnered 6800+ happy customers with 97%+ client retention rate.
Looking for professional JavaScript web app development services? We provide custom JavaScript development services applying latest version frameworks and libraries to propel businesses to the next level. Our well-defined and manageable JS development processes are balanced between cost, time and quality along with clear communication.
Our JavaScript development companies offers you strict NDA, 100% money back guarantee and agile/DevOps approach.
#javascript development company #javascript development services #javascript web development #javascript development #javascript web development services #javascript web development company
1616670795
It is said that a digital resource a business has must be interactive in nature, so the website or the business app should be interactive. How do you make the app interactive? With the use of JavaScript.
Does your business need an interactive website or app?
Hire Dedicated JavaScript Developer from WebClues Infotech as the developer we offer is highly skilled and expert in what they do. Our developers are collaborative in nature and work with complete transparency with the customers.
The technology used to develop the overall app by the developers from WebClues Infotech is at par with the latest available technology.
Get your business app with JavaScript
For more inquiry click here https://bit.ly/31eZyDZ
Book Free Interview: https://bit.ly/3dDShFg
#hire dedicated javascript developers #hire javascript developers #top javascript developers for hire #hire javascript developer #hire a freelancer for javascript developer #hire the best javascript developers
1632537859
Not babashka. Node.js babashka!?
Ad-hoc CLJS scripting on Node.js.
Experimental. Please report issues here.
Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.
Additional goals and features are:
Nbb requires Node.js v12 or newer.
CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).
Install nbb
from NPM:
$ npm install nbb -g
Omit -g
for a local install.
Try out an expression:
$ nbb -e '(+ 1 2 3)'
6
And then install some other NPM libraries to use in the script. E.g.:
$ npm install csv-parse shelljs zx
Create a script which uses the NPM libraries:
(ns script
(:require ["csv-parse/lib/sync$default" :as csv-parse]
["fs" :as fs]
["path" :as path]
["shelljs$default" :as sh]
["term-size$default" :as term-size]
["zx$default" :as zx]
["zx$fs" :as zxfs]
[nbb.core :refer [*file*]]))
(prn (path/resolve "."))
(prn (term-size))
(println (count (str (fs/readFileSync *file*))))
(prn (sh/ls "."))
(prn (csv-parse "foo,bar"))
(prn (zxfs/existsSync *file*))
(zx/$ #js ["ls"])
Call the script:
$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs
Nbb has first class support for macros: you can define them right inside your .cljs
file, like you are used to from JVM Clojure. Consider the plet
macro to make working with promises more palatable:
(defmacro plet
[bindings & body]
(let [binding-pairs (reverse (partition 2 bindings))
body (cons 'do body)]
(reduce (fn [body [sym expr]]
(let [expr (list '.resolve 'js/Promise expr)]
(list '.then expr (list 'clojure.core/fn (vector sym)
body))))
body
binding-pairs)))
Using this macro we can look async code more like sync code. Consider this puppeteer example:
(-> (.launch puppeteer)
(.then (fn [browser]
(-> (.newPage browser)
(.then (fn [page]
(-> (.goto page "https://clojure.org")
(.then #(.screenshot page #js{:path "screenshot.png"}))
(.catch #(js/console.log %))
(.then #(.close browser)))))))))
Using plet
this becomes:
(plet [browser (.launch puppeteer)
page (.newPage browser)
_ (.goto page "https://clojure.org")
_ (-> (.screenshot page #js{:path "screenshot.png"})
(.catch #(js/console.log %)))]
(.close browser))
See the puppeteer example for the full code.
Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet
macro is similar to promesa.core/let
.
$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)' 0.17s user 0.02s system 109% cpu 0.168 total
The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx
this adds another 300ms or so, so for faster startup, either use a globally installed nbb
or use $(npm bin)/nbb script.cljs
to bypass npx
.
Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.
To load .cljs
files from local paths or dependencies, you can use the --classpath
argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs
relative to your current dir, then you can load it via (:require [foo.bar :as fb])
. Note that nbb
uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar
in the namespace name becomes foo_bar
in the directory name.
To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:
$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"
and then feed it to the --classpath
argument:
$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]
Currently nbb
only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar
files will be added later.
The name of the file that is currently being executed is available via nbb.core/*file*
or on the metadata of vars:
(ns foo
(:require [nbb.core :refer [*file*]]))
(prn *file*) ;; "/private/tmp/foo.cljs"
(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"
Nbb includes reagent.core
which will be lazily loaded when required. You can use this together with ink to create a TUI application:
$ npm install ink
ink-demo.cljs
:
(ns ink-demo
(:require ["ink" :refer [render Text]]
[reagent.core :as r]))
(defonce state (r/atom 0))
(doseq [n (range 1 11)]
(js/setTimeout #(swap! state inc) (* n 500)))
(defn hello []
[:> Text {:color "green"} "Hello, world! " @state])
(render (r/as-element [hello]))
Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core
namespace is included with the let
and do!
macros. An example:
(ns prom
(:require [promesa.core :as p]))
(defn sleep [ms]
(js/Promise.
(fn [resolve _]
(js/setTimeout resolve ms))))
(defn do-stuff
[]
(p/do!
(println "Doing stuff which takes a while")
(sleep 1000)
1))
(p/let [a (do-stuff)
b (inc a)
c (do-stuff)
d (+ b c)]
(prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3
Also see API docs.
Since nbb v0.0.75 applied-science/js-interop is available:
(ns example
(:require [applied-science.js-interop :as j]))
(def o (j/lit {:a 1 :b 2 :c {:d 1}}))
(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1
Most of this library is supported in nbb, except the following:
:syms
.-x
notation. In nbb, you must use keywords.See the example of what is currently supported.
See the examples directory for small examples.
Also check out these projects built with nbb:
See API documentation.
See this gist on how to convert an nbb script or project to shadow-cljs.
Prequisites:
To build:
bb release
Run bb tasks
for more project-related tasks.
Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb
License: EPL-1.0
#node #javascript
1616671994
If you look at the backend technology used by today’s most popular apps there is one thing you would find common among them and that is the use of NodeJS Framework. Yes, the NodeJS framework is that effective and successful.
If you wish to have a strong backend for efficient app performance then have NodeJS at the backend.
WebClues Infotech offers different levels of experienced and expert professionals for your app development needs. So hire a dedicated NodeJS developer from WebClues Infotech with your experience requirement and expertise.
So what are you waiting for? Get your app developed with strong performance parameters from WebClues Infotech
For inquiry click here: https://www.webcluesinfotech.com/hire-nodejs-developer/
Book Free Interview: https://bit.ly/3dDShFg
#hire dedicated node.js developers #hire node.js developers #hire top dedicated node.js developers #hire node.js developers in usa & india #hire node js development company #hire the best node.js developers & programmers
1621250665
Looking to hire dedicated top Reactjs developers at affordable prices? Our 5+ years of average experienced Reactjs developers comprise proficiency in delivering the most complex and challenging web apps.
Hire ReactJS developers online on a monthly, hourly, or full-time basis who are highly skilled & efficient in implementing new technologies and turn into business-driven applications while saving your cost up to 60%.
Planning to** outsource React web Development services from India** using Reactjs? Or would you like to hire a team of Reactjs developers? Get in touch for a free quote!
#hire react js developer #react.js developer #react.js developers #hire reactjs development company #react js development india #react js developer