All the code snippets on this page are live and interactive powered by the klipse plugin.
[ ≡ | Tutorial | Reference | Examples | Background | GitHub | Try Lenses! ]
Lenses are basically a bidirectional composable abstraction for updating selected elements of immutable data structures that admits efficient implementation. This library provides a collection of partial isomorphisms, lenses, and traversals, collectively known as optics, for manipulating JSON and users can write new optics for manipulating non-JSON objects, such as Immutable.js collections. A partial lens can view optional data, insert new data, update existing data and remove existing data and can, for example, provide defaults and maintain required data structure parts. Try Lenses!
L.collect(traversal, maybeData) ~> [...values]
L.collectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> [...values]
L.foldl((value, maybeValue, index) => value, value, traversal, maybeData) ~> value
L.foldr((value, maybeValue, index) => value, value, traversal, maybeData) ~> value
L.maximum(traversal, maybeData) ~> maybeValue
L.minimum(traversal, maybeData) ~> maybeValue
L.product(traversal, maybeData) ~> number
L.sum(traversal, maybeData) ~> number
Let's work with the following sample JSON object:
var sampleTexts = {
contents: [{ language: "en", text: "Title" },
{ language: "sv", text: "Rubrik" }]
}
First we import libraries
import * as L from "partial.lenses"
import * as R from "ramda"
and compose a parameterized lens for accessing texts:
var textIn = language => L.compose(L.prop("contents"),
L.required([]),
L.normalize(R.sortBy(R.prop("language"))),
L.find(R.whereEq({language})),
L.defaults({language}),
L.prop("text"),
L.valueOr(""))
Take a moment to read through the above definition line by line. Each part
either specifies a step in the path to select the desired element or a way in
which the data structure must be treated at that point. The purpose of
the L.prop(...)
parts is probably obvious. The other parts we will
mention below.
Thanks to the parameterized search
part, L.find(R.whereEq({language}))
, of the lens composition, we
can use it to query texts:
L.get(textIn("sv"), sampleTexts)
L.get(textIn("en"), sampleTexts)
Partial lenses can deal with missing data. If we use the partial lens to query a text that does not exist, we get the default:
L.get(textIn("fi"), sampleTexts)
We get this value, rather than undefined
, thanks to the last
part, L.valueOr("")
, of our lens composition, which ensures that
we get the specified value rather than null
or undefined
. We get the
default even if we query from undefined
:
L.get(textIn("fi"), undefined)
With partial lenses, undefined
is the equivalent of empty or non-existent.
As with ordinary lenses, we can use the same lens to update texts:
L.set(textIn("en"), "The title", sampleTexts)
The same partial lens also allows us to insert new texts:
L.set(textIn("fi"), "Otsikko", sampleTexts)
Note the position into which the new text was inserted. The array of texts is
kept sorted thanks to
the L.normalize(R.sortBy(R.prop("language")))
part of our
lens.
Finally, we can use the same partial lens to remove texts:
L.set(textIn("sv"), undefined, sampleTexts)
Note that a single text is actually a part of an object. The key to having the
whole object vanish, rather than just the text
property, is
the L.defaults({language})
part of our lens composition.
A L.defaults(valueIn)
lens works symmetrically. When set
with valueIn
, the result is undefined
, which means that the focus of the
lens is to be removed.
If we remove all of the texts, we get the required value:
R.pipe(L.set(textIn("sv"), undefined),
L.set(textIn("en"), undefined))(sampleTexts)
The contents
property is not removed thanks to the
L.required([])
part of our lens
composition. L.required
is the dual
of L.defaults
. L.defaults
replaces
undefined
values when viewed and L.required
replaces
undefined
values when set.
Note that unless default and required values are explicitly specified as part of
the lens, they will both be undefined
.
Take out one (or
more)
L.required(...)
,
L.normalize(...)
, L.defaults(...)
or L.valueOr(...)
part(s) from the lens composition and try to
predict what happens when you rerun the examples with the modified lens
composition. Verify your reasoning by actually rerunning the examples.
Replace L.defaults(...)
with L.valueOr(...)
or
vice verse and try to predict what happens when you rerun the examples with the
modified lens composition. Verify your reasoning by actually rerunning the
examples.
For clarity, the previous code snippets avoided some of the shorthands that this library supports. In particular,
L.compose(...)
can be abbreviated as an array
[...]
,L.prop(string)
can be abbreviated as string
, andL.set(l, undefined, s)
can be abbreviated
as L.remove(l, s)
.It is also typical to compose lenses out of short paths following the schema of the JSON data being manipulated. Recall the lens from the start of the example:
L.compose(L.prop("contents"),
L.required([]),
L.normalize(R.sortBy(R.prop("language"))),
L.find(R.whereEq({language})),
L.defaults({language}),
L.prop("text"),
L.valueOr(""))
Following the structure or schema of the JSON, we could break this into three separate lenses:
Furthermore, we could organize the lenses into an object following the structure of the JSON:
var Texts = {
data: {
contents: ["contents",
L.required([]),
L.normalize(R.sortBy(R.prop("language")))]
},
contents: {
contentIn: language => [L.find(R.whereEq({language})),
L.defaults({language})]
},
content: {
text: ["text", L.valueOr("")]
},
textIn: language => [Texts.data.contents,
Texts.contents.contentIn(language),
Texts.content.text]
}
We can now say:
L.get(Texts.textIn("sv"), sampleTexts)
This style of organizing lenses is overkill for our toy example. In a more
realistic case the sampleTexts
object would contain many more properties.
Also, rather than composing a lens, like Texts.textIn
above, to access a leaf
property from the root of our object, we might actually compose lenses
incrementally as we inspect the model structure.
So far we have used a lens to manipulate individual items. This library also supports traversals that compose with lenses and can target multiple items. Continuing on the tutorial example, let's define a traversal that targets all the texts:
var texts = ["contents",
L.required([]),
L.elems,
L.choose(R.pipe(L.remove("text"), L.defaults)),
"text"]
What makes the above a traversal is the L.elems
part. Once a
traversal is composed with a lens, the whole results is a traversal. The other
parts of the above composition should already be familiar from previous
examples. Note the use of L.choose
with L.defaults
. We'll get back to that shortly.
Now, we can use the above traversal to collect
all the texts:
L.collect(texts, sampleTexts)
More generally, we can map and fold over texts. For example, we can compute the length of the longest text:
var Max = {empty: () => 0, concat: Math.max}
L.concatAs(R.length, Max, texts, sampleTexts)
Of course, we can also modify texts. For example, we could uppercase all the titles:
L.modify(texts, R.toUpper, sampleTexts)
We can also set and remove texts. Recall the L.choose
and L.defaults
combination from the definition of texts
. Like with the textIn
lens, that
part allows us to remove the whole object instead of just the text
property:
L.remove(texts, sampleTexts)
We can also manipulate texts selectively. For example, we could truncate all the texts that are longer than 5 characters:
L.modify([texts, L.when(t => t.length > 5)],
t => t.slice(0, 5) + "...",
sampleTexts)
The combinators provided by this library are available as named imports. Typically one just imports the library as:
import * as L from "partial.lenses"
The abstractions, traversals, lenses, and isomorphisms, provided by this library are collectively known as optics. Traversals can target any number of elements. Lenses are a restriction of traversals that target a single element. Isomorphisms are a restriction of lenses with an inverse.
Some optics libraries provide many more abstractions, such as "optionals", "prisms" and "folds", to name a few, forming a DAG. Aside from being conceptually important, many of those abstractions are not only useful but required in a statically typed setting where data structures have precise constraints on their shapes, so to speak, and operations on data structures must respect those constraints at all times.
In a dynamically typed language like JavaScript, the shapes of run-time objects
are naturally malleable. Nothing immediately breaks if a new object is
created as a copy of another object by adding or removing a property, for
example. We can exploit this to our advantage by considering all optics as
partial. A partial optic, as manifested in this library, may be intended to
operate on data structures of a specific type, such as arrays or objects, but
also accepts the possibility that it may be given any valid JSON object or
undefined
as input. When the input does not match the expectation of a
partial lens, the input is treated as being undefined
. This allows specific
partial optics, such as the simple L.prop
lens, to be used in a
wider range of situations than corresponding total optics.
L.modify(optic, (maybeValue, index) => maybeValue, maybeData) ~> maybeData
L.modify
allows one to map over the focused element
L.modify(["elems", 0, "x"], R.inc, {elems: [{x: 1, y: 2}, {x: 3, y: 4}]})
or, when using a traversal, elements
L.modify(["elems", L.elems, "x"],
R.dec,
{elems: [{x: 1, y: 2}, {x: 3, y: 4}]})
of a data structure.
L.remove(optic, maybeData) ~> maybeData
L.remove
allows one to remove the focused element
L.remove([0, "x"], [{x: 1}, {x: 2}, {x: 3}])
or, when using a traversal, elements
L.remove([L.elems, "x", L.when(x => x > 1)], [{x: 1}, {x: 2, y: 1}, {x: 3}])
from a data structure.
Note that L.remove(optic, maybeData)
is equivalent
to L.set(lens, undefined, maybeData)
. With partial lenses, setting
to undefined
typically has the effect of removing the focused element.
L.set(optic, maybeValue, maybeData) ~> maybeData
L.set
allows one to replace the focused element
L.set(["a", 0, "x"], 11, {id: "z"})
or, when using a traversal, elements
L.set([L.elems, "x", L.when(x => x > 1)], -1, [{x: 1}, {x: 2, y: 1}, {x: 3}])
of a data structure.
Note that L.set(lens, maybeValue, maybeData)
is equivalent
to L.modify(lens, R.always(maybeValue), maybeData)
.
L.compose(...optics) ~> optic
or [...optics]
L.compose
performs composition of optics. The following equations
characterize composition:
L.compose() = L.identity
L.compose(l) = l
L.modify(L.compose(o, ...os)) = R.compose(L.modify(o), ...os.map(L.modify))
L.get(L.compose(o, ...os)) = R.pipe(L.get(o), ...os.map(L.get))
Furthermore, in this library, an array of optics [...optics]
is treated as a
composition L.compose(...optics)
. Using the array notation, the above
equations can be written as:
[] = L.identity
[l] = l
L.modify([o, ...os]) = R.compose(L.modify(o), ...os.map(L.modify))
L.get([o, ...os]) = R.pipe(L.get(o), ...os.map(L.get))
For example:
L.set(["a", 1], "a", {a: ["b", "c"]})
L.get(["a", 1], {a: ["b", "c"]})
Note that R.compose
is not the same as
L.compose
.
L.chain((value, index) => optic, optic) ~> optic
L.chain(toOptic, optic)
is equivalent to
L.compose(optic, L.choose((maybeValue, index) =>
maybeValue === undefined
? L.zero
: toOptic(maybeValue, index)))
Note that with the L.just
, L.chain
, L.choice
and L.zero
combinators, one can consider optics as subsuming the
maybe monad.
L.choice(...lenses) ~> optic
L.choice
returns a partial optic that acts like the first of the given lenses
whose view is not undefined
on the given data structure. When the views of
all of the given lenses are undefined
, the returned lens acts
like L.zero
, which is the identity element of L.choice
.
For example:
L.modify([L.elems, L.choice("a", "d")], R.inc, [{R: 1}, {a: 1}, {d: 2}])
L.choose((maybeValue, index) => optic) ~> optic
L.choose
creates an optic whose operation is determined by the given function
that maps the underlying view, which can be undefined
, to an optic. In other
words, the L.choose
combinator allows an optic to be constructed after
examining the data structure being manipulated.
For example, given:
var majorAxis =
L.choose(({x, y} = {}) => Math.abs(x) < Math.abs(y) ? "y" : "x")
we get:
L.get(majorAxis, {x: 1, y: 2})
L.get(majorAxis, {x: -3, y: 1})
L.modify(majorAxis, R.negate, {x: 2, y: -3})
L.optional ~> optic
L.optional
is an optic over an optional element. When used as a traversal,
and the focus is undefined
, the traversal is empty. When used as a lens, and
the focus is undefined
, the lens will be read-only.
As an example, consider the difference between:
L.set([L.elems, "x"], 3, [{x: 1}, {y: 2}])
and:
L.set([L.elems, "x", L.optional], 3, [{x: 1}, {y: 2}])
Note that L.optional
is equivalent
to L.when(x => x !== undefined)
.
L.when((maybeValue, index) => testable) ~> optic
L.when
allows one to selectively skip elements within a traversal or to
selectively turn a lens into a read-only lens whose view is undefined
.
For example:
L.modify([L.elems, L.when(x => x > 0)], R.negate, [0, -1, 2, -3, 4])
Note that L.when(p)
is equivalent
to L.choose((x, i) => p(x, i) ? L.identity : L.zero)
.
L.zero ~> optic
L.zero
is the identity element of L.choice
and L.chain
. As a traversal, L.zero
is a traversal of no
elements and as a lens, i.e. when used with L.get
, L.zero
is a
read-only lens whose view is always undefined
.
For example:
L.collect([L.elems,
L.choose(x => (R.is(Array, x) ? L.elems :
R.is(Object, x) ? "x" :
L.zero))],
[1, {x: 2}, [3,4]])
L.lazy(optic => optic) ~> optic
L.lazy
can be used to construct optics lazily. The function given to L.lazy
is passed a forwarding proxy to its return value and can also make forward
references to other optics and possibly construct a recursive optic.
Note that when using L.lazy
to construct a recursive optic, it will only work
in a meaningful way when the recursive uses are at nested positions meaning that
the recursive use is precomposed with some other optic.
For example, here is a traversal that targets all the primitive elements in a data structure of nested arrays and objects:
var flatten = [L.optional, L.lazy(rec => {
const elems = [L.elems, rec]
const values = [L.values, rec]
return L.choose(x => (x instanceof Array ? elems :
x instanceof Object ? values :
L.identity))
})]
Note that the above creates a cyclic representation of the traversal.
Now, for example:
L.collect(flatten, [[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
L.modify(flatten, x => x+1, [[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
L.remove([flatten, L.when(x => 3 <= x && x <= 4)],
[[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
L.log(...labels) ~> optic
L.log(...labels)
is an identity optic that
outputs
console.log
messages with the given labels
(or
format in Node.js)
when data flows in either direction, get
or set
, through the lens.
For example:
L.get(["x", L.log()], {x: 10})
L.set(["x", L.log("x")], "11", {x: 10})
L.set(["x", L.log("%s x: %j")], "11", {x: 10})
L.toFunction(optic) ~> optic
L.toFunction
converts a given optic, which can be a string,
an integer, an array, or a function to a function.
This can be useful for implementing new combinators and operations that cannot
otherwise be implemented using the combinators provided by this library.
For isomorphisms and lenses, the returned function will have the signature
(Functor c, (Maybe a, Index) -> c b, Maybe s, Index) -> c t
and for traversals the signature will be
(Applicative c, (Maybe a, Index) -> c b, Maybe s, Index) -> c t
Note that the above signatures are written using the "tupled" parameter notation
(...) -> ...
to denote that the functions are not curried.
The
Functor
and
Applicative
arguments
are expected to conform to
their
Static Land
specifications.
Note that, in conjunction with partial optics, it may be advantageous to have
the Functor
and Applicative
algebras to allow for partiality. With
traversals it is also possible, for example, to simply post compose optics
with L.optional
to eliminate undefined
elements.
A traversal operates over a collection of focuses that can, for example, be collected, folded, modified, set and removed.
L.concat(monoid, traversal, maybeData) ~> traversal
L.concat({empty, concat}, t, s)
performs a fold, using the given concat
and
empty
operations, over the elements focused on by the given traversal or lens
t
from the given data structure s
. The concat
operation and the constant
returned by empty()
should form
a
monoid over
the values focused on by t
.
For example:
var Sum = {empty: () => 0, concat: (x, y) => x + y}
L.concat(Sum, L.elems, [1, 2, 3])
Note that L.concat
is staged so that after given the first argument,
L.concat(m)
, a computation step is performed.
See also: L.merge
.
L.concatAs((maybeValue, index) => value, monoid, traversal, maybeData) ~> traversal
L.concatAs(xMi2r, {empty, concat}, t, s)
performs a map, using given function
xMi2r
, and fold, using the given concat
and empty
operations, over the
elements focused on by the given traversal or lens t
from the given data
structure s
. The concat
operation and the constant returned by empty()
should form
a
monoid over
the values returned by xMi2r
.
For example:
L.concatAs(x => x, Sum, L.elems, [1, 2, 3])
Note that L.concatAs
is staged so that after given the first two arguments,
L.concatAs(f, m)
, a computation step is performed.
See also: L.mergeAs
.
L.merge(monoid, traversal, maybeData) ~> traversal
L.merge({empty, concat}, t, s)
performs a fold, using the given concat
and
empty
operations, over the elements focused on by the given traversal or lens
t
from the given data structure s
. The concat
operation and the constant
returned by empty()
should form
a
commutative monoid over
the values focused on by t
.
For example:
L.merge(Sum, L.elems, [1, 2, 3])
Note that L.merge
is staged so that after given the first argument,
L.merge(m)
, a computation step is performed.
See also: L.concat
.
L.mergeAs((maybeValue, index) => value, monoid, traversal, maybeData) ~> traversal
L.mergeAs(xMi2r, {empty, concat}, t, s)
performs a map, using given function
xMi2r
, and fold, using the given concat
and empty
operations, over the
elements focused on by the given traversal or lens t
from the given data
structure s
. The concat
operation and the constant returned by empty()
should form
a
commutative monoid over
the values returned by xMi2r
.
For example:
L.mergeAs(x => x, Sum, L.elems, [1, 2, 3])
Note that L.mergeAs
is staged so that after given the first two arguments,
L.mergeAs(f, m)
, a computation step is performed.
Note that L.mergeAs
is generally the fastest way to fold over a traversal,
because it maps to the internal implementation most directly.
See also: L.concatAs
.
L.collect(traversal, maybeData) ~> [...values]
L.collect
returns an array of the defined elements focused on by the given
traversal or lens from a data structure.
For example:
L.collect(["xs", L.elems, "x"], {xs: [{x: 1}, {x: 2}]})
Note that L.collect
is equivalent
to L.collectAs(R.identity)
.
L.collectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> [...values]
L.collectAs
returns an array of the elements focused on by the given traversal
or lens from a data structure and mapped by the given function to a defined
value. Given a lens, there will be 0 or 1 elements in the returned array. Note
that a partial lens always targets an element, but L.collectAs
implicitly
skips elements that are mapped to undefined
by the given function. Given a
traversal, there can be any number of elements in the array returned by
L.collectAs
.
For example:
L.collectAs(R.negate, ["xs", L.elems, "x"], {xs: [{x: 1}, {x: 2}]})
L.collectAs(toMaybe, traversal, maybeData)
is equivalent
to
L.concatAs(R.pipe(toMaybe, toCollect), Collect, traversal, maybeData)
where
Collect
and toCollect
are defined as follows:
var Collect = {empty: R.always([]), concat: R.concat}
var toCollect = x => x !== undefined ? [x] : []
So:
L.concatAs(R.pipe(R.negate, toCollect),
Collect,
["xs", L.elems, "x"],
{xs: [{x: 1}, {x: 2}]})
The internal implementation of L.collectAs
is optimized and faster than the
above naïve implementation.
L.foldl((value, maybeValue, index) => value, value, traversal, maybeData) ~> value
L.foldl
performs a fold from left over the elements focused on by the given
traversal.
For example:
L.foldl((x, y) => x + y, 0, L.elems, [1,2,3])
L.foldr((value, maybeValue, index) => value, value, traversal, maybeData) ~> value
L.foldr
performs a fold from right over the elements focused on by the given
traversal.
For example:
L.foldr((x, y) => x * y, 1, L.elems, [1,2,3])
L.maximum(traversal, maybeData) ~> maybeValue
L.maximum
computes a maximum, according to the >
operator, of the optional
elements targeted by the traversal.
For example:
L.maximum(L.elems, [1,2,3])
L.minimum(traversal, maybeData) ~> maybeValue
L.minimum
computes a minimum, according to the <
operator, of the optional
elements targeted by the traversal.
For example:
L.minimum(L.elems, [1,2,3])
L.product(traversal, maybeData) ~> number
L.product
computes the product of the optional numbers targeted by the
traversal.
For example:
L.product(L.elems, [1,2,3])
L.sum(traversal, maybeData) ~> number
L.sum
computes the sum of the optional numbers targeted by the traversal.
For example:
L.sum(L.elems, [1,2,3])
L.branch({prop: traversal, ...props}) ~> traversal
L.branch
creates a new traversal from a given template object that specifies
how the new traversal should visit the properties of an object.
For example:
L.collect(L.branch({first: L.elems, second: L.identity}),
{first: ["x"], second: "y"})
Note that you can also compose L.branch
with other optics. For example, you
can compose with L.pick
to create a traversal over specific
elements of an array:
L.modify([L.pick({x: 0, z: 2}),
L.branch({x: L.identity, z: L.identity})],
R.negate,
[1, 2, 3])
See the BST traversal section for a more meaningful example.
L.elems ~> traversal
L.elems
is a traversal over the elements of an array-like
object. When written through, L.elems
always produces an Array
.
For example:
L.modify(["xs", L.elems, "x"], R.inc, {xs: [{x: 1}, {x: 2}]})
Just like with other optics operating on array-like objects, when
manipulating non-Array
objects, L.rewrite
can be used to
convert the result to the desired type, if necessary:
L.modify([L.rewrite(xs => Int8Array.from(xs)), L.elems],
R.inc,
Int8Array.from([-1,4,0,2,4]))
L.values ~> traversal
L.values
is a traversal over the values of an instanceof Object
. When
written through, L.values
always produces an Object
.
For example:
L.modify(L.values, R.negate, {a: 1, b: 2, c: 3})
When manipulating objects with a non-Object
constructor
function XYZ(x,y,z) {
this.x = x
this.y = y
this.z = z
}
XYZ.prototype.norm = function () {
return (this.x * this.x +
this.y * this.y +
this.z * this.z)
}
L.rewrite
can be used to convert the result to the desired type,
if necessary:
var objectTo = R.curry((C, o) => Object.assign(Object.create(C.prototype), o))
L.modify([L.rewrite(objectTo(XYZ)), L.values],
R.negate,
new XYZ(1,2,3))
Lenses always have a single focus which can be viewed directly.
L.get(lens, maybeData) ~> maybeValue
L.get
returns the focused element from a data structure.
For example:
L.get("y", {x: 112, y: 101})
Note that L.get
does not work on traversals.
L.lens((maybeData, index) => maybeValue, (maybeValue, maybeData, index) => maybeData) ~> lens
L.lens
creates a new primitive lens. The first parameter is the getter and
the second parameter is the setter. The setter takes two parameters: the
first is the value written and the second is the data structure to write into.
One should think twice before introducing a new primitive lens—most of the
combinators in this library have been introduced to reduce the need to write new
primitive lenses. With that said, there are still valid reasons to create new
primitive lenses. For example, here is a lens that we've used in production,
written with the help of Moment.js, to bidirectionally
convert a pair of start
and end
times to a duration:
var timesAsDuration = L.lens(
({start, end} = {}) => {
if (undefined === start)
return undefined
if (undefined === end)
return "Infinity"
return moment.duration(moment(end).diff(moment(start))).toJSON()
},
(duration, {start = moment().toJSON()} = {}) => {
if (undefined === duration || "Infinity" === duration) {
return {start}
} else {
return {
start,
end: moment(start).add(moment.duration(duration)).toJSON()
}
}
}
)
Now, for example:
L.get(timesAsDuration,
{start: "2016-12-07T09:39:02.451Z",
end: moment("2016-12-07T09:39:02.451Z").add(10, "hours").toISOString()})
L.set(timesAsDuration,
"PT10H",
{start: "2016-12-07T09:39:02.451Z",
end: "2016-12-07T09:39:02.451Z"})
When composed with L.pick
, to flexibly pick the start
and end
times, the above can be adapted to work in a wide variety of cases. However,
the above lens will never be added to this library, because it would require
adding dependency to Moment.js.
See the Interfacing with Immutable.js section for another
example of using L.lens
.
L.augment({prop: object => value, ...props}) ~> lens
L.augment
is given a template of functions to compute new properties. When
not viewing or setting a defined object, the result is undefined
. When
viewing a defined object, the object is extended with the computed properties.
When set with a defined object, the extended properties are removed.
For example:
L.modify(L.augment({y: r => r.x + 1}),
r => ({x: r.x + r.y, y: 2, z: r.x - r.y}),
{x: 1})
L.defaults(valueIn) ~> lens
L.defaults
is used to specify a default context or value for an element in
case it is missing. When set with the default value, the effect is to remove
the element. This can be useful for both making partial lenses with propagating
removal and for avoiding having to check for and provide default values
elsewhere.
For example:
L.get(["items", L.defaults([])], {})
L.get(["items", L.defaults([])], {items: [1, 2, 3]})
L.set(["items", L.defaults([])], [], {items: [1, 2, 3]})
Note that L.defaults(valueIn)
is equivalent
to L.replace(undefined, valueIn)
.
L.define(value) ~> lens
L.define
is used to specify a value to act as both the default value and the
required value for an element.
L.get(["x", L.define(null)], {y: 10})
L.set(["x", L.define(null)], undefined, {y: 10})
Note that L.define(value)
is equivalent to [L.required(value),
L.defaults(value)]
.
L.normalize((value, index) => value) ~> lens
L.normalize
maps the value with same given transform when viewed and set and
implicitly maps undefined
to undefined
.
One use case for normalize
is to make it easy to determine whether, after a
change, the data has actually changed. By keeping the data normalized, a
simple R.equals
comparison will do.
Note that the difference between L.normalize
and L.rewrite
is
that L.normalize
applies the transform in both directions
while L.rewrite
only applies the transform when writing.
L.required(valueOut) ~> lens
L.required
is used to specify that an element is not to be removed; in case it
is removed, the given value will be substituted instead.
For example:
L.remove(["items", 0], {items: [1]})
L.remove([L.required({}), "items", 0], {items: [1]})
L.remove(["items", L.required([]), 0], {items: [1]})
Note that L.required(valueOut)
is equivalent
to L.replace(valueOut, undefined)
.
L.rewrite((valueOut, index) => valueOut) ~> lens
L.rewrite
maps the value with the given transform when set and implicitly maps
undefined
to undefined
. One use case for rewrite
is to re-establish data
structure invariants after changes.
Note that the difference between L.normalize
and L.rewrite
is that L.normalize
applies the transform in both directions
while L.rewrite
only applies the transform when writing.
See the BST as a lens section for a meaningful example.
Objects that have a non-negative integer length
and strings, which are not
considered Object
instances in JavaScript, are considered array-like objects
by partial optics.
When writing a defined value through an optic that operates on array-like
objects, the result is always an Array
. For example:
L.set(1, "a", "LoLa")
It may seem like the result should be of the same type as the object being
manipulated, but that is problematic, because the focus of a partial optic is
always optional. Instead, when manipulating strings or array-like non-Array
objects, L.rewrite
can be used to convert the result to the
desired type, if necessary. For example:
L.set([L.rewrite(R.join("")), 1], "a", "LoLa")
L.append ~> lens
L.append
is a write-only lens that can be used to append values to
an array-like object. The view of L.append
is always
undefined
.
For example:
L.get(L.append, ["x"])
L.set(L.append, "x", undefined)
L.set(L.append, "x", ["z", "y"])
Note that L.append
is equivalent to L.index(i)
with the index
i
set to the length of the focused array or 0 in case the focus is not a
defined array.
L.filter((value, index) => testable) ~> lens
L.filter
operates on array-like objects. When not viewing an
array-like object, the result is undefined
. When viewing an array-like
object, only elements matching the given predicate will be returned. When set,
the resulting array will be formed by concatenating the elements of the set
array-like object and the elements of the complement of the filtered focus. If
the resulting array would be empty, the whole result will be undefined
.
For example:
L.set(L.filter(x => x <= "2"), "abcd", "3141592")
NOTE: If you are merely modifying a data structure, and don't need to limit
yourself to lenses, consider using the L.elems
traversal composed
with L.when
.
An alternative design for filter could implement a smarter algorithm to combine
arrays when set. For example, an algorithm based
on edit distance could be used to
maintain relative order of elements. While this would not be difficult to
implement, it doesn't seem to make sense, because in most cases use
of L.normalize
or L.rewrite
would be
preferable. Also, the L.elems
traversal composed
with L.when
will retain order of elements.
L.find((value, index) => testable) ~> lens
L.find
operates on array-like objects
like L.index
, but the index to be viewed is determined by finding
the first element from the focus that matches the given predicate. When no
matching element is found the effect is same as with L.append
.
L.remove(L.find(x => x <= 2), [3,1,4,1,5,9,2])
L.findWith(...lenses) ~> lens
L.findWith(...lenses)
chooses an index from an array-like
object through which the given lens, [...lenses]
, focuses on a
defined item and then returns a lens that focuses on that item.
For example:
L.get(L.findWith("x"), [{z: 6}, {x: 9}, {y: 6}])
L.set(L.findWith("x"), 3, [{z: 6}, {x: 9}, {y: 6}])
L.index(elemIndex) ~> lens
or elemIndex
L.index(elemIndex)
or just elemIndex
focuses on the element at specified
index of an array-like object.
undefined
.undefined
, the element is removed from the resulting array,
shifting all higher indices down by one. If the result would be an empty
array, the whole result will be undefined
.undefined
.For example:
L.set(2, "z", ["x", "y", "c"])
NOTE: There is a gotcha related to removing elements from array-like
objects. Namely, when the last element is removed, the result is undefined
rather than an empty array. This is by design, because this allows the removal
to propagate upwards. It is not uncommon, however, to have cases where removing
the last element from an array-like object must not remove the array itself.
Consider the following examples without L.required([])
:
L.remove(0, ["a", "b"])
L.remove(0, ["b"])
L.remove(["elems", 0], {elems: ["b"], some: "thing"})
Then consider the same examples with L.required([])
:
L.remove([L.required([]), 0], ["a", "b"])
L.remove([L.required([]), 0], ["b"])
L.remove(["elems", L.required([]), 0], {elems: ["b"], some: "thing"})
There is a related gotcha with L.required
. Consider the
following example:
L.remove(L.required([]), [])
L.get(L.required([]), [])
In other words, L.required
works in both directions. Thanks to
the handling of undefined
within partial lenses, this is often not a problem,
but sometimes you need the "default" value both ways. In that case you can
use L.define
.
L.slice(maybeBegin, maybeEnd) ~> lens
L.slice
focuses on a specified range of elements of
an array-like object. The range is determined like with the
standard
slice
method
of arrays, basically
undefined
gives the defaults: 0 for the begin and length for the end.For example:
L.get(L.slice(1, -1), [1,2,3,4])
L.set(L.slice(-2, undefined), [0], [1,2,3,4])
L.prop(propName) ~> lens
or propName
L.prop(propName)
or just propName
focuses on the specified object property.
undefined
.Object
.undefined
, the property is removed from the result.
If the result would be an empty object, the whole result will be undefined
.When setting or removing properties, the order of keys is preserved.
For example:
L.get("y", {x: 1, y: 2, z: 3})
L.set("y", -2, {x: 1, y: 2, z: 3})
When manipulating objects whose constructor is not
Object
, L.rewrite
can be used to convert the result to the
desired type, if necessary:
L.set([L.rewrite(objectTo(XYZ)), "z"], 3, new XYZ(3,1,4))
L.props(...propNames) ~> lens
L.props
focuses on a subset of properties of an object, allowing one to treat
the subset of properties as a unit. The view of L.props
is undefined
when
none of the properties is defined. Otherwise the view is an object containing a
subset of the properties. Setting through L.props
updates the whole subset of
properties, which means that any missing properties are removed if they did
exists previously. When set, any extra properties are ignored.
L.set(L.props("x", "y"), {x: 4}, {x: 1, y: 2, z: 3})
Note that L.props(k1, ..., kN)
is equivalent to L.pick({[k1]: k1, ..., [kN]:
kN})
.
L.valueOr(valueOut) ~> lens
L.valueOr
is an asymmetric lens used to specify a default value in case the
focus is undefined
or null
. When set, L.valueOr
behaves like the identity
lens.
For example:
L.get(L.valueOr(0), null)
L.set(L.valueOr(0), 0, 1)
L.remove(L.valueOr(0), 1)
L.orElse(backupLens, primaryLens) ~> lens
L.orElse(backupLens, primaryLens)
acts like primaryLens
when its view is not
undefined
and otherwise like backupLens
. You can use L.orElse
on its own
with R.reduceRight
(and R.reduce
) to create an associative
choice over lenses or use L.orElse
to specify a default or backup lens
for L.choice
, for example.
L.just(maybeValue) ~> lens
L.just
returns a read-only lens whose view is always the given value. In
other words, for all x
, y
and z
:
L.get(L.just(z), x) = z
L.set(L.just(z), y, x) = x
Note that L.just(x)
is equivalent to L.to(R.always(x))
.
L.just
can be seen as the unit function of the monad formed
with L.chain
.
L.to((maybeValue, index) => maybeValue) ~> lens
L.to
creates a read-only lens whose view is determined by the given function.
For example:
L.get(["x", L.to(x => x + 1)], {x: 1})
L.set(["x", L.to(x => x + 1)], 3, {x: 1})
L.pick({prop: lens, ...props}) ~> lens
L.pick
creates a lens out of the given object template of lenses and allows
one to pick apart a data structure and then put it back together. When viewed,
an object is created, whose properties are obtained by viewing through the
lenses of the template. When set with an object, the properties of the object
are set to the context via the lenses of the template. undefined
is treated
as the equivalent of empty or non-existent in both directions.
For example, let's say we need to deal with data and schema in need of some semantic restructuring:
var sampleFlat = {px: 1, py: 2, vx: 1.0, vy: 0.0}
We can use L.pick
to create lenses to pick apart the data and put it back
together into a more meaningful structure:
var asVec = prefix => L.pick({x: prefix + "x", y: prefix + "y"})
var sanitize = L.pick({pos: asVec("p"), vel: asVec("v")})
We now have a better structured view of the data:
L.get(sanitize, sampleFlat)
That works in both directions:
L.modify([sanitize, "pos", "x"], R.add(5), sampleFlat)
NOTE: In order for a lens created with L.pick
to work in a predictable
manner, the given lenses must operate on independent parts of the data
structure. As a trivial example, in L.pick({x: "same", y: "same"})
both of
the resulting object properties, x
and y
, address the same property of the
underlying object, so writing through the lens will give unpredictable results.
Note that, when set, L.pick
simply ignores any properties that the given
template doesn't mention. Also note that the underlying data structure need not
be an object.
L.replace(maybeValueIn, maybeValueOut) ~> lens
L.replace(maybeValueIn, maybeValueOut)
, when viewed, replaces the value
maybeValueIn
with maybeValueOut
and vice versa when set.
For example:
L.get(L.replace(1, 2), 1)
L.set(L.replace(1, 2), 2, 0)
The main use case for replace
is to handle optional and required properties
and elements. In most cases, rather than using replace
, you will make
selective use of defaults
, required
and define
.
The focus of an isomorphism is the whole data structure rather than a part of
it. Furthermore, an isomorphism can be inverted. More
specifically, a lens, iso
, is an isomorphism iff the following equations hold
for all x
and y
in the domain and range, respectively, of the lens:
L.set(iso, L.get(iso, x), undefined) = x
L.get(iso, L.set(iso, y, undefined)) = y
The above equations mean that x => L.get(iso, x)
and y => L.set(iso, y,
undefined)
are inverses of each other.
L.getInverse(isomorphism, maybeData) ~> maybeData
L.getInverse
views through an isomorphism in the inverse direction.
For example:
var numeric = f => x => typeof x === "number" ? f(x) : undefined
var offBy1 = L.iso(numeric(R.inc), numeric(R.dec))
L.getInverse(offBy1, 1)
Note that L.getInverse(iso, data)
is equivalent
to L.set(iso, data, undefined)
.
Also note that, while L.getInverse
makes most sense when used with an
isomorphism, it is valid to use L.getInverse
with partial lenses in general.
Doing so essentially constructs a minimal data structure that contains the given
value. For example:
L.getInverse("meaning", 42)
L.iso(maybeData => maybeValue, maybeValue => maybeData) ~> isomorphism
L.iso
creates a new primitive isomorphism.
For example:
var negate = L.iso(numeric(R.negate), numeric(R.negate))
L.get([negate, L.inverse(negate)], 112)
L.identity ~> isomorphism
L.identity
is the identity element of lens composition and also the identity
isomorphism. The following equations characterize L.identity
:
L.get(L.identity, x) = x
L.modify(L.identity, f, x) = f(x)
L.compose(L.identity, l) = l
L.compose(l, L.identity) = l
L.inverse(isomorphism) ~> isomorphism
L.inverse
returns the inverse of the given isomorphism. Note that this
operation only makes sense on isomorphisms.
For example:
L.get(L.inverse(offBy1), 1)
Note that if you are new to lenses, then you probably want to start with the tutorial.
A case that we have run into multiple times is where we have an array of constant strings such as
var sampleFlags = ["id-19", "id-76"]
that we wish to manipulate as if it was a collection of boolean flags. Here is a parameterized lens that does just that:
var flag = id => [L.normalize(R.sortBy(R.identity)),
L.find(R.equals(id)),
L.replace(undefined, false),
L.replace(id, true)]
Now we can treat individual constants as boolean flags:
L.get(flag("id-69"), sampleFlags)
L.get(flag("id-76"), sampleFlags)
In both directions:
L.set(flag("id-69"), true, sampleFlags)
L.set(flag("id-76"), false, sampleFlags)
Binary search trees might initially seem to be outside the scope of definable
lenses. However, given basic BST operations, one could easily wrap them as a
primitive partial lens. But could we leverage lens combinators to build a BST
lens more compositionally? We can. The L.choose
combinator
allows for dynamic construction of lenses based on examining the data structure
being manipulated. Inside L.choose
we can write the ordinary BST
logic to pick the correct branch based on the key in the currently examined node
and the key that we are looking for. So, here is our first attempt at a BST
lens:
var searchAttempt = key => L.lazy(rec => {
const smaller = ["smaller", rec]
const greater = ["greater", rec]
const found = L.defaults({key})
return L.choose(n => {
if (!n || key === n.key)
return found
return key < n.key ? smaller : greater
})
})
var valueOfAttempt = key => [searchAttempt(key), "value"]
Note that we also make use of the L.lazy
combinator to create a
recursive lens with a cyclic representation.
This actually works to a degree. We can use the valueOfAttempt
lens
constructor to build a binary tree. Here is a little helper to build a tree
from pairs:
var fromPairs =
R.reduce((t, [k, v]) => L.set(valueOfAttempt(k), v, t), undefined)
Now:
var sampleBST = fromPairs([[3, "g"], [2, "a"], [1, "m"], [4, "i"], [5, "c"]])
sampleBST
However, the above searchAttempt
lens constructor does not maintain the BST
structure when values are being removed:
L.remove(valueOfAttempt(3), sampleBST)
How do we fix this? We could check and transform the data structure to a BST
after changes. The L.rewrite
combinator can be used for that
purpose. Here is a naïve rewrite to fix a tree after value removal:
var naiveBST = L.rewrite(n => {
if (undefined !== n.value) return n
const s = n.smaller, g = n.greater
if (!s) return g
if (!g) return s
return L.set(search(s.key), s, g)
})
Here is a working search
lens and a valueOf
lens constructor:
var search = key => L.lazy(rec => {
const smaller = ["smaller", rec]
const greater = ["greater", rec]
const found = L.defaults({key})
return [naiveBST, L.choose(n => {
if (!n || key === n.key)
return found
return key < n.key ? smaller : greater
})]
})
var valueOf = key => [search(key), "value"]
Now we can also remove values from a binary tree:
L.remove(valueOf(3), sampleBST)
As an exercise, you could improve the rewrite to better maintain balance.
Perhaps you might even enhance it to maintain a balance condition such
as AVL
or Red-Black. Another
worthy exercise would be to make it so that the empty binary tree is null
rather than undefined
.
What about traversals over BSTs? We can use
the L.branch
combinator to define an in-order traversal over the
values of a BST:
var values = L.lazy(rec => [
L.optional,
naiveBST,
L.branch({smaller: rec,
value: L.identity,
greater: rec})])
Given a binary tree sampleBST
we can now manipulate it as a whole. For
example:
var Concat = {empty: () => "", concat: R.concat}
L.concatAs(R.toUpper, Concat, values, sampleBST)
L.modify(values, R.toUpper, sampleBST)
L.remove([values, L.when(x => x > "e")], sampleBST)
Immutable.js is a popular library providing immutable data structures. As argued in Lenses with Immutable.js it can be useful to wrap such libraries as optics.
When interfacing external libraries with partial lenses one does need to consider whether and how to support partiality. Partial lenses allow one to insert new and remove existing elements rather than just view and update existing elements.
List
indexingHere is a primitive partial lens for
indexing List
written
using L.lens
:
var getList = i => xs => Immutable.List.isList(xs) ? xs.get(i) : undefined
var setList = i => (x, xs) => {
if (!Immutable.List.isList(xs))
xs = Immutable.List()
if (x !== undefined)
return xs.set(i, x)
xs = xs.delete(i)
return xs.size ? xs : undefined
}
var idxList = i => L.lens(getList(i), setList(i))
Note how the above uses isList
to check the input. When viewing, in case the
input is not a List
, the proper result is undefined
. When updating the
proper way to handle a non-List
is to treat it as empty and also to replace a
resulting empty list with undefined
. Also, when updating, we treat
undefined
as a request to delete
rather than set
.
We can now view existing elements:
var sampleList = Immutable.List(["a", "l", "i", "s", "t"])
L.get(idxList(2), sampleList)
Update existing elements:
L.modify(idxList(1), R.toUpper, sampleList)
Remove existing elements:
L.remove(idxList(0), sampleList)
And removing the last element propagates removal:
L.remove(["elems", idxList(0)],
{elems: Immutable.List(["x"]), look: "No elems!"})
We can also create lists from non-lists:
L.set(idxList(0), "x", undefined)
And we can also append new elements:
L.set(idxList(5), "!", sampleList)
Consider what happens when the index given to idxList
points further beyond
the last element. Both the L.index
lens and the above lens add
undefined
values, which is not ideal with partial lenses, because of the
special treatment of undefined
. In practise, however, it is not typical to
set
elements except to append just after the last element.
Fortunately we do not need Immutable.js data structures to provide a compatible
partial
traverse
function
to support traversals, because it is also possible to implement
traversals simply by providing suitable isomorphisms between Immutable.js data
structures and JSON. Here is a partial isomorphism between
List
and arrays:
var fromList = xs => Immutable.List.isList(xs) ? xs.toArray() : undefined
var toList = xs => R.is(Array, xs) && xs.length ? Immutable.List(xs) : undefined
var isoList = L.iso(fromList, toList)
So, now we can compose a traversal over List
as:
var seqList = [isoList, L.elems]
And all the usual operations work as one would expect, for example:
L.remove([seqList, L.when(c => c < "i")], sampleList)
And:
L.concatAs(R.toUpper,
Concat,
[seqList, L.when(c => c <= "i")],
sampleList)
Consider the following REPL session using Ramda:
R.set(R.lensPath(["x", "y"]), 1, {})
R.set(R.compose(R.lensProp("x"), R.lensProp("y")), 1, {})
R.view(R.lensPath(["x", "y"]), {})
R.view(R.compose(R.lensProp("x"), R.lensProp("y")), {})
R.set(R.lensPath(["x", "y"]), undefined, {x: {y: 1}})
R.set(R.compose(R.lensProp("x"), R.lensProp("y")), undefined, {x: {y: 1}})
One might assume that R.lensPath([p0,
...ps])
is equivalent to
R.compose(R.lensProp(p0), ...ps.map(R.lensProp))
, but that is not the case.
With partial lenses you can robustly compose a path lens from prop
lenses L.compose(L.prop(p0), ...ps.map(L.prop))
or just use the
shorthand notation [p0, ...ps]
. In JavaScript, missing (and
mismatching) data can be mapped to undefined
, which is what partial lenses
also do, because undefined
is not a valid JSON value.
When a part of a data structure is missing, an attempt to view it returns
undefined
. When a part is missing, setting it to a defined value inserts the
new part. Setting an existing part to undefined
removes it.
There are several lens and optics libraries for JavaScript. In this section I'd like to very briefly elaborate on a number design choices made during the course of developing this library.
Making all optics partial allows optics to not only view and update existing elements, but also to insert, replace (as in replace with data of different type) and remove elements and to do so in a seamless and efficient way. In a library based on total lenses, one needs to e.g. explicitly compose lenses with prisms to deal with partiality. This not only makes the optic compositions more complex, but can also have a significant negative effect on performance.
The downside of implicit partiality is the potential to create incorrect optics that signal errors later than when using total optics.
JSON is the data-interchange format of choice today. By being able to effectively and efficiently manipulate JSON data structures directly, one can avoid using special internal representations of data and make things simpler (e.g. no need to convert from JSON to efficient immutable collections and back).
undefined
undefined
is a natural choice in JavaScript, especially when dealing with
JSON, to represent nothingness. Some libraries use null
, but that is arguably
a poor choice, because null
is a valid JSON value. Some libraries implement
special Maybe
types, but the benefits do not seem worth the trouble. First of
all, undefined
already exists in JavaScript and is not a valid JSON value.
Inventing a new value to represent nothingness doesn't seem to add much. OTOH,
wrapping values with Just
objects introduces a significant performance
overhead due to extra allocations. Operations with optics do not otherwise
necessarily require large numbers of allocations and can be made highly
efficient.
Not having an explicit Just
object means that dealing with values such as
Just Nothing
requires special consideration.
Aside from the brevity, allowing strings and non-negative integers to be directly used as optics allows one to avoid allocating closures for such optics. This can provide significant time and, more importantly, space savings in applications that create large numbers of lenses to address elements in data structures.
The downside of allowing such special values as optics is that the internal implementation needs to be careful to deal with them at any point a user given value needs to be interpreted as an optic.
Aside from the brevity, treating an array of optics as a composition allows the
library to be optimized to deal with simple paths highly efficiently and
eliminate the need for separate primitives
like assocPath
and dissocPath
for performance reasons.
Client code can also manipulate such simple paths as data.
One interesting consequence of partiality is that it becomes possible to invert isomorphisms without explicitly making it possible to extract the forward and backward functions from an isomorphism. A simple internal implementation based on functors and applicatives seems to be expressive enough for a wide variety of operations.
L.branch
By providing combinators for creating new traversals, lenses and isomorphisms,
client code need not depend on the internal implementation of optics. The
current version of this library exposes the internal implementation
via L.toFunction
, but it would not be unreasonable to not
provide such an operation. Only very few applications need to know the internal
representation of optics.
Indexing in partial lenses is unnested, very simple and based on the indices and keys of the underlying data structures. When indexing was added, it essentially introduced no performance degradation, but since then a few operations have been added that do require extra allocations to support indexing. It is also possible to compose optics so as to create nested indices or paths, but currently no combinator is directly provided for that.
The algebraic structures used in partial lenses follow the Static Land specification rather than the Fantasy Land specification. Static Land does not require wrapping values in objects, which translates to a significant performance advantage throughout the library, because fewer allocations are required.
Concern for performance has been a part of the work on partial lenses for some time. The basic principles can be summarized in order of importance:
Here are a few benchmark results on partial lenses (as L
version 9.0.2) and
some roughly equivalent operations using Ramda (as R
version 0.23.0), Ramda Lens (as P
version 0.1.1), and Flunc Optics (as O
version 0.0.2). As always with benchmarks, you should take these numbers with a
pinch of salt and preferably try and measure your actual use cases!
7,429,258/s 1.00x R.reduceRight(add, 0, xs100)
464,228/s 16.00x L.foldr(add, 0, L.elems, xs100)
4,168/s 1782.37x O.Fold.foldrOf(O.Traversal.traversed, addC, 0, xs100)
11,245/s 1.00x R.reduceRight(add, 0, xs100000)
56/s 200.96x L.foldr(add, 0, L.elems, xs100000)
0/s Infinityx O.Fold.foldrOf(O.Traversal.traversed, addC, 0, xs100000) -- STACK OVERFLOW
678,969/s 1.00x L.foldl(add, 0, L.elems, xs100)
211,793/s 3.21x R.reduce(add, 0, xs100)
3,002/s 226.17x O.Fold.foldlOf(O.Traversal.traversed, addC, 0, xs100)
4,064,819/s 1.00x L.sum(L.elems, xs100)
541,416/s 7.51x L.merge(Sum, L.elems, xs100)
127,340/s 31.92x R.sum(xs100)
23,422/s 173.55x P.sumOf(P.traversed, xs100)
4,298/s 945.74x O.Fold.sumOf(O.Traversal.traversed, xs100)
574,523/s 1.00x L.maximum(L.elems, xs100)
3,362/s 170.86x O.Fold.maximumOf(O.Traversal.traversed, xs100)
151,447/s 1.00x L.sum([L.elems, L.elems, L.elems], xsss100)
147,450/s 1.03x L.merge(Sum, [L.elems, L.elems, L.elems], xsss100)
4,238/s 35.73x P.sumOf(R.compose(P.traversed, P.traversed, P.traversed), xsss100)
877/s 172.61x O.Fold.sumOf(R.compose(O.Traversal.traversed, O.Traversal.traversed, O.Traversal.traversed), xsss100)
255,796/s 1.00x L.collect(L.elems, xs100)
3,517/s 72.73x O.Fold.toListOf(O.Traversal.traversed, xs100)
115,728/s 1.00x L.collect([L.elems, L.elems, L.elems], xsss100)
9,067/s 12.76x R.chain(R.chain(R.identity), xsss100)
804/s 143.92x O.Fold.toListOf(R.compose(O.Traversal.traversed, O.Traversal.traversed, O.Traversal.traversed), xsss100)
66,523/s 1.00x R.flatten(xsss100)
37,087/s 1.79x L.collect(flatten, xsss100)
18,463,205/s 1.00x L.modify(L.elems, inc, xs)
1,881,362/s 9.81x R.map(inc, xs)
426,892/s 43.25x P.over(P.traversed, inc, xs)
404,660/s 45.63x O.Setter.over(O.Traversal.traversed, inc, xs)
425,113/s 1.00x L.modify(L.elems, inc, xs1000)
119,449/s 3.56x R.map(inc, xs1000)
403/s 1055.55x O.Setter.over(O.Traversal.traversed, inc, xs1000) -- QUADRATIC
366/s 1160.07x P.over(P.traversed, inc, xs1000) -- QUADRATIC
157,507/s 1.00x L.modify([L.elems, L.elems, L.elems], inc, xsss100)
9,806/s 16.06x R.map(R.map(R.map(inc)), xsss100)
3,514/s 44.82x P.over(R.compose(P.traversed, P.traversed, P.traversed), inc, xsss100)
2,935/s 53.66x O.Setter.over(R.compose(O.Traversal.traversed, O.Traversal.traversed, O.Traversal.traversed), inc, xsss100)
32,101,122/s 1.00x L.get(1, xs)
3,946,374/s 8.13x R.nth(1, xs)
1,587,369/s 20.22x R.view(l_1, xs)
23,329,807/s 1.00x L.set(1, 0, xs)
6,990,165/s 3.34x R.update(1, 0, xs)
982,638/s 23.74x R.set(l_1, 0, xs)
28,303,132/s 1.00x L.get("y", xyz)
24,002,161/s 1.18x R.prop("y", xyz)
2,450,501/s 11.55x R.view(l_y, xyz)
12,342,395/s 1.00x L.set("y", 0, xyz)
7,479,499/s 1.65x R.assoc("y", 0, xyz)
1,293,679/s 9.54x R.set(l_y, 0, xyz)
14,561,179/s 1.00x L.get([0,"x",0,"y"], axay)
14,189,439/s 1.03x R.path([0,"x",0,"y"], axay)
2,311,151/s 6.30x R.view(l_0x0y, axay)
485,852/s 29.97x R.view(l_0_x_0_y, axay)
4,615,467/s 1.00x L.set([0,"x",0,"y"], 0, axay)
833,456/s 5.54x R.assocPath([0,"x",0,"y"], 0, axay)
523,766/s 8.81x R.set(l_0x0y, 0, axay)
325,620/s 14.17x R.set(l_0_x_0_y, 0, axay)
4,371,655/s 1.00x L.modify([0,"x",0,"y"], inc, axay)
545,705/s 8.01x R.over(l_0x0y, inc, axay)
336,918/s 12.98x R.over(l_0_x_0_y, inc, axay)
24,363,305/s 1.00x L.remove(1, xs)
2,953,233/s 8.25x R.remove(1, 1, xs)
13,321,942/s 1.00x L.remove("y", xyz)
2,654,914/s 5.02x R.dissoc("y", xyz)
16,234,629/s 1.00x L.get(["x","y","z"], xyzn)
14,246,092/s 1.14x R.path(["x","y","z"], xyzn)
2,313,952/s 7.02x R.view(l_xyz, xyzn)
789,871/s 20.55x R.view(l_x_y_z, xyzn)
166,313/s 97.61x O.Getter.view(o_x_y_z, xyzn)
5,593,457/s 1.00x L.set(["x","y","z"], 0, xyzn)
1,411,586/s 3.96x R.assocPath(["x","y","z"], 0, xyzn)
714,821/s 7.82x R.set(l_xyz, 0, xyzn)
522,355/s 10.71x R.set(l_x_y_z, 0, xyzn)
218,509/s 25.60x O.Setter.set(o_x_y_z, 0, xyzn)
4,079,906/s 1.00x L.remove(50, xs100)
1,817,392/s 2.24x R.remove(50, 1, xs100)
4,260,008/s 1.00x L.set(50, 2, xs100)
1,697,027/s 2.51x R.update(50, 2, xs100)
687,213/s 6.20x R.set(l_50, 2, xs100)
Various operations on partial lenses have been optimized for common cases, but there is definitely a lot of room for improvement. The goal is to make partial lenses fast enough that performance isn't the reason why you might not want to use them.
See bench.js for details.
As said in the first sentence of this document, lenses are convenient for performing updates on individual elements of immutable data structures. Having abilities such as nesting, adapting, recursing and restructuring using lenses makes the notion of an individual element quite flexible and, even further, traversals make it possible to selectively target zero or more elements of non-trivial data structures in a single operation. It can be tempting to try to do everything with lenses, but that will likely only lead to misery. It is important to understand that lenses are just one of many functional abstractions for working with data structures and sometimes other approaches can lead to simpler or easier solutions. Zippers, for example, are, in some ways, less principled and can implement queries and transforms that are outside the scope of lenses and traversals.
One type of use case which we've ran into multiple times and falls out of the sweet spot of lenses is performing uniform transforms over data structures. For example, we've run into the following use cases:
One approach to making such whole data structure spanning updates is to use a simple bottom-up transform. Here is a simple implementation for JSON based on ideas from the Uniplate library:
var descend = (w2w, w) => R.is(Object, w) ? R.map(w2w, w) : w
var substUp = (h2h, w) => descend(h2h, descend(w => substUp(h2h, w), w))
var transform = (w2w, w) => w2w(substUp(w2w, w))
transform(w2w, w)
basically just performs a single-pass bottom-up transform
using the given function w2w
over the given data structure w
. Suppose we
are given the following data:
var sampleBloated = {
just: "some",
extra: "crap",
that: [
"we",
{want: "to",
filter: ["out"],
including: {the: "following",
extra: true,
fields: 1}}]
}
We can now remove the extra
fields
like this:
transform(R.ifElse(R.allPass([R.is(Object), R.complement(R.is(Array))]),
L.remove(L.props("extra", "fields")),
R.identity),
sampleBloated)
Lenses are an old concept and there are dozens of academic papers on lenses and dozens of lens libraries for various languages. Here are just a few links:
Feel free to suggest more links!


Servers can be found with branding ES&CS, under community servers and modified servers. At the moment only hosting dedicated European servers.
High-performance servers hosted from Finland, active admin support, montly wipe cycle and full blown toxic community.
You thought you can earn rewards for your vote? Nope vote us for nexus of nothingness and equilibrium of the end.
rust.servers.netHelp us keep servers fun for everyone and report suspected hackers. Report hackers directly to Facepunch or contact us at Discord.
facepunch


Before using this demo, you must create a ddoc for each user you wish to test with. Default user is called `ad`. The script for creating users is avaialble at https://github.com/alxndrsn/medic-webapp/blob/ddoc-per-user/scripts/new_user_ddoc
Below are multiple approaches to fetching data from Couch to Pouch.
If you're benchmarking, please close your Developer Tools - Networking, Console and other tags may significantly slow execution.


Class: Rack::Deflater
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Inherits: | Object |
| Defined in: | lib/rack/deflater.rb |
Overview
This middleware enables content encoding of http responses, usually for purposes of compression.
Currently supported encodings:
-
gzip
-
identity (no transformation)
This middleware automatically detects when encoding is supported and allowed. For example no encoding is made when a cache directive of ‘no-transform’ is present, when the response status code is one that doesn’t allow an entity body, or when the body is empty.
Note that despite the name, Deflater
does not support the deflate
encoding.
Class Method Summary
-
.new(app, options = {}) ⇒ Deflater
constructor
Creates
Deflater
middleware.
Instance Method Summary
- #call(env)
-
#should_deflate?(env, status, headers, body) ⇒ Boolean
private
Whether the body should be compressed.
Constructor Details
.new(app, options = {}) ⇒ Deflater
Creates Deflater
middleware. Options:
:if
-
a lambda enabling / disabling deflation based on returned boolean value
(e.g <tt>use Rack::Deflater, :if => lambda { |*, body| sum=0; body.each { |i| sum += i.length }; sum > 512 }</tt>).
However, be aware that calling `body.each` inside the block will break cases where `body.each` is not idempotent,
such as when it is an IO instance.
:include
-
a list of content types that should be compressed. By default, all content types are compressed.
:sync
-
determines if the stream is going to be flushed after every chunk. Flushing after every chunk reduces
latency for time-sensitive streaming applications, but hurts compression and throughput.
Defaults to true.
# File 'lib/rack/deflater.rb', line 39
def initialize(app, = {}) @app = app @condition = [:if] @compressible_types = [:include] @sync = .fetch(:sync, true) end
Instance Method Details
#call(env)
[ GitHub ]# File 'lib/rack/deflater.rb', line 46
def call(env) status, headers, body = response = @app.call(env) unless should_deflate?(env, status, headers, body) return response end request = Request.new(env) encoding = Utils.select_best_encoding(%w(gzip identity), request.accept_encoding) # Set the Vary HTTP header. vary = headers["vary"].to_s.split(",").map(&:strip) unless vary.include?("*") || vary.any?{|v| v.downcase == 'accept-encoding'} headers["vary"] = vary.push("Accept-Encoding").join(",") end case encoding when "gzip" headers['content-encoding'] = "gzip" headers.delete(CONTENT_LENGTH) mtime = headers["last-modified"] mtime = Time.httpdate(mtime).to_i if mtime response[2] = GzipStream.new(body, mtime, @sync) response when "identity" response else # when nil # Only possible encoding values here are 'gzip', 'identity', and nil = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." bp = Rack::BodyProxy.new([ ]) { body.close if body.respond_to?(:close) } [406, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => .length.to_s }, bp] end end
#should_deflate?(env, status, headers, body) ⇒ Boolean
(private)
Whether the body should be compressed.
# File 'lib/rack/deflater.rb', line 136
def should_deflate?(env, status, headers, body) # Skip compressing empty entity body responses and responses with # no-transform set. if Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) || /\bno-transform\b/.match?(headers[CACHE_CONTROL].to_s) || headers['content-encoding']&.!~(/\bidentity\b/) return false end # Skip if @compressible_types are given and does not include request's content type return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/])) # Skip if @condition lambda is given and evaluates to false return false if @condition && !@condition.call(env, status, headers, body) # No point in compressing empty body, also handles usage with # Rack::Sendfile. return false if headers[CONTENT_LENGTH] == '0' true end


StatusPage embed widget
You can use the JavaScript and CSS files from this project to embed any current maintenance or incidents from a StatusPage instance. You can restrict this down to specific components if you know their ID.
The JavaScript file hooks into any HTML element with the class of statuspage-embed-container
so it can be loaded asynchronously or placed at the end of the
.
Examples
Full University of Warwick StatusPage embed:

Restricting to just a few components:

Data attributes
data-api-base
- the fully qualified base URI, e.g.https://status.warwick.ac.uk/api
data-components
- a JSON array of component IDs - see /api/v2/components.json for a list of component IDsdata-poll-interval
- how often (in milliseconds) the current summary status should be polled. Defaults to60000
(i.e. 1 minute)data-position
- where the widget should be positioned when displayed,bl
,br
,tl
, ortr
. Defaults tobl
(bottom left). Note that on mobile,bl
andbr
are treated as bottom andtl
andtr
as top.data-font-awesome-variant
- which variant of FontAwesome icons should be used,fas
(solid),far
(regular),fal
(light), orfad
(duotone). Defaults tofas
(solid) - note that any other option requires a Pro subscription to FontAwesome.
Content Security Policy
The widget will require a connect-src
directive for the API base specified.
Source code and license
The widget is licensed under the ISC license. Source code is available on GitHub.


All Stores
All Brands
Wipe Database
Test Store 2
Brands At This Store
Remove Brand
Available Brands
Test Brand - $20.00
Add Brand
Change Store Name
Change Name
Update Name
Delete This Store
Delete This Store


Chapter 1: How to read this book?
Chapter 2: What is STxT?
- What is STxT?
- Statement of Intent :-)
- What does STxT mean?
- Why a new language?
- A language for everyone
- Chaotic Statement of Intent :-D (**)
- Applications, results, and expectations
Chapter 3: STxT, Tutorial
- One example... now!
- EVERYTHING in Namespaces (ns)
- Nodes and indentation
- Node and Text
- Comments
- Finally, grammar
- Overview
Chapter 4: Creating and Using Grammars
- Namespaces and grammars (*)
- Basic rule (*)
- One example to start with
- How Namespaces are created and read
- Nodes of the NODE type
- Text nodes
- Numerical Nodes
- Nodes with Bits
- One example to finish off
- One last thought (*)
- Grammar of the grammar (**)
Chapter 5: Compaction (*)
Chapter 6: Advanced Tutorial (*)
Chapter 7: Equivalences and Internationalization (I18N)
Chapter 8: Examples and Other Technologies
Chapter 9: STxT and XML
- Note for the reader
- The legend (**)
- Nowadays (**)
- Initial contact
- XML attributes
- DTD, XSD
- Parsers and validators
- Namespaces
- Future


Back in university I did my honours project on Virtual Textures. Mega Textures could be considered the predecessor to Virtual Textures. It’s actually a very simple idea, and was surprisingly easy to knock up a simple application.
What Is It
In short it is a dynamic mip-map system. With a handful of 512x512 textures you can texture a massively unique terrain. With a very small memory overhead. If you can imagine drawing rings around the camera that go further and further out. Each one of these rings is a 512x512 texture that stretches further and further away. A notable example in games was Quake Wars, but the idea was around well before John Carmack made it popular.
How Does It Work
In this demo I created a big texture and also created all the mip layers up-front, this makes it easier to sample on the fly without having to think about things like filtering as well. HDD space is cheap, memory, CPU and GPU are less cheap.
So for a given point we build a new mip map layer.
The different colors in the screen shot indicate a different texture (mip) being sampled around the origin (note: you would usually do this around the camera).
Limitations
It is 2D really. Mega Textures fail with overhangs and things like that. Which means it can’t be extended to texturing other objects you may have in the world. However its easy! So for texturing large areas like we do in GIS it might be handy.
In this screenshot the textures are being sampled, which results in seemless transition between the mips.
My Solution
The code for the demo can be found here, its very limited and doesn’t rebuild the maps when you move the camera, although that should be trivial enough to add. I’m also using 6 mips as I had in mind mapping of very very large areas, but I was having an issue making a large enough dataset so 6 mips might seem like an overkill for what is being renderered. The dataset isn’t included in the repo as Github doesn’t like files of 1gb. However you can easily open up your favourite image editor and make a massive texture, be sure to udpate the size of the texture in the code.
Final
Virtual Textures are far superior, and can help abstract alot of your texture management, but for a cheap texturing solution this is still viable. I’d like to take another crack at Virtual Textures time permitting but I might try and do it in a map context rather than a game one.


rice wheat yield change climate basin model increase temperature water SWAT rainfall .–. crop growing irrigation mean future different calibration scenarios River using projected (Fig. growth production respectively. decrease validation {border-style Gomti Water impact India data emission px;} reported used #ffffff;} changes higher period simulation > .–., Climate Singh basin. compared range season yields area scenarios. varied days periods simulated study time upstream < evapotranspiration gauging irrigated land management streamflow Fig. HRUs cropping downstream results stress use However, baseline basin, impacts maximum performance IGB) PBIAS max number respectively respectively, showed watershed (Table Agric Clim Rice Soil adaptation based basins calibrated considered daily effects greater hydrological likely map models phase plant productivity px; simulating soil values {font-family Aggarwal Assessment Change Ganga Kumar Wheat aquifer cover entire font-size increased monthly ranged statistics Environ Mishra Uttar Williams a). actual agricultural analysis application average canal development districts double; food located minimum pattern practices resources shallow stations studies validated yield. (Neitsch (upper (–), Gosain India. Institute Jaunpur MIROC Maighat Model NSE Naresh Res Research Resour Rosenzweig Sci Therefore, Tool \documentclass[pt]{minimal} \setlength{\oddsidemargin}{-pt} \usepackage{amsbsy} \usepackage{amsfonts} \usepackage{amsmath} \usepackage{amssymb} \usepackage{mathrsfs} \usepackage{upgreek} \usepackage{wasysym} air allocation assessment border-color border-width crops distribution district factors global model. month observed parameters performed periods. reasonably region rice, river scenario scenarios, sensitive slightly solid; threshold tstrs wise {font-style {font-weight °C, (< (lower (t/ha) )^{} Arnold Das Further, HadRM Indo-Gangetic International Islam Lakshmanan Lucknow, National Plains REMO RSR SCS Shah Srinivasan Sultanpur Sultanpur, T_{opt} Texas [], \bar{T}_{avg} \begin{document}$${\text{tstrs}} \quad affect assigned attributed available b). biomass bold case category century coefficient courier; decreased depending developing differences distributed em; etc. factor good harvesting high hydrologic hydrology initial long main median optimal optimum period, precipitation projections pt;} remained response shown simulates source sub sugarcane surface systems temperatures term test used. version


He had a name I could never remember.
He'd pull a coin from behind my ear
then hold it under his tongue all through dinner.

I pressed my ear flat against his chest, listening
to his lungs. That summer, I got so drunk
the card deck of my hands fell from my sleeves.
I kissed Death out of my best friend's naked
breasts, then tried to jack him out of my throat
using the palliative neck of a broken Popov bottle.
I once swallowed enough sand to sleep
for three straight days. My body was like a tomb
some old Pharaoh's ghost had staked out, telling me
how to go and stay at the same damn time. Then the man
with a name I could never remember handed back
what I'd let him carry for my whole life: the note he kept
in his pocket that held my name, written by my own hand.


R/AllGenerics.R
, R/components.R
, R/colormap.R
, and 1 more
i2dashboard-content.Rd
Content can be added to the dashboards pages, the sidebar or the navigation bar.
add_component(dashboard, component, ...) add_to_sidebar(dashboard, component, ...) add_colormap(dashboard, ...) add_link(dashboard, ...) # S4 method for i2dashboard,character add_component(dashboard, component, page = "default", copy = FALSE, ...) # S4 method for i2dashboard,`function` add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard,gg add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard,gt_tbl add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard,kableExtra add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard,Heatmap add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard,ANY add_component(dashboard, component, page = "default", ...) # S4 method for i2dashboard add_link( dashboard, href, title = NULL, icon = NULL, align = c("right", "left"), target = NULL ) # S4 method for i2dashboard add_colormap(dashboard, map, name) # S4 method for i2dashboard,character add_to_sidebar( dashboard, component, page = "default", global = FALSE, copy = FALSE, ... ) # S4 method for i2dashboard,`function` add_to_sidebar( dashboard, component, page = "default", global = FALSE, copy = FALSE, ... )
| dashboard | A i2dashboard. |
|---|---|
| component | An R object, function, or string. |
| ... | Additional parameters passed to the components render function. In case of an image, parameters |
| page | The name of the page to add the component or sidebar to. |
| copy | Whether or not to copy images to |
| href | The target of the link. |
| title | The link title. |
| icon | An optional link icon (see https://rmarkdown.rstudio.com/flexdashboard/using.html#icon-sets) |
| align | Optional argument that can be “left” or “right” (defaults = “right”) defining the alignment of the links in the navigation bar |
| target | An optional target (e.g. "_blank") |
| map | A character vector containing colors and possible the levels they map to (as names). |
| name | A name for the color mapping. |
| global | Whether or not to add the content to the global sidebar. |
Returns the modified i2dashboard object.
'add_component()
' extends the list of components of the respective page, stored in the 'pages
' slot, by an R Markdown string containing the provided content.
'add_to_sidebar()
' extends either the 'sidebar
' slot or the 'sidebar
' entry of a single page by an R Markdown string containing the provided content.
'add_link()
' extends the 'navbar
' slot by a list of link properties.
'add_colormap()
' extends the 'colormaps
' slot by the new color mapping.
If something went wrong during the addition, the unmodified i2dashboard object is returned.
The options to add content in detail:
'add_component()
' adds content to a page of the dashboard by evaluating a function, or by including an object, a text or image file.
'add_to_sidebar()
' adds content to the dashboards global sidebar or to a pages local sidebar.
'add_link()
' adds a link to the navigation bar.
'add_colormap()
' adds a global color mapping to the dashboards colormaps.
The mechanism to add different types of content to a dashboards page or sidebar depends on the class of the object passed to the function 'add_component()
' or 'add_to_sidebar()
':
A function will be evaluated and its return value is used as content.
A string that ends with .md
or .txt
will be used to open a file and use its content.
A string that ends with \.[png|jpg|jpeg|gif]
will be used to include an image as content.
An R object (e.g. an 'htmlwidget') will be included if a suitable signature method is implemented.
library(magrittr) i2dashboard() -> dashboard myFunction <- function(dashboard) paste0("### Generate component\n\n", "Lorem ipsum dolor sit amet\n") dashboard %<>% add_component(component = myFunction) dashboard %<>% add_component(component = plotly::plot_ly(mtcars, x=~wt, y=~hp), title = "Include htmlwidget") # \donttest{ dashboard %<>% add_component(component = "sample.txt", title = "Include text") dashboard %<>% add_component(component = "sample.jpg", title = "Include image") # } dashboard %<>% add_to_sidebar(component = myFunction) # \donttest{ dashboard %<>% add_to_sidebar(component = "sample.txt", package="i2dash") dashboard %<>% add_to_sidebar(component = "sample.jpg", package="i2dash") # } colors <- c("l1" = "#F7FCFD", "l2" ="#E5F5F9", "l3" = "#CCECE6") dashboard %<>% add_colormap(map = colors, name = "test") dashboard %<>% add_link(href = "www.sample_url.net", title = "MyLink", align = "left")


1v1.LOL Clan Central
A Discord that unites all 1v1.LOL active clans and players to do tournaments/clan wars.
The largest 1v1.LOL community server to date!
What do we provide?
Over 2,000 members
We're well above 2K members and we appreciate every member who's in the server because you have supported 1CC.
Read moreMore than 10 verified clans
We have over 10 verified clans, come verify your clan or join a clan to represent it in our clan tournaments.
Read moreCompetitive tournaments
There is a variety of tournaments for different regions and devices that we provide every season.
Read moreGiveaways sponsored by game developers
Thanks to the 1v1.LOL developers, we are hosting some Discord Nitro giveaways each month in our Discord server.
Read moreHuge cash prizes & weekly events
In our weekly events, we not only want people to get noticed and earn roles, but earn CASH as well, so we provide a cash prize in most of our events.
Read more


Buy KEC International; target of Rs 335: ICICI Direct
ICICI Direct is bullish on KEC International has recommended buy rating on the stock with a target price of Rs 335 in its research report dated November 11, 2019.
by Broker ResearchICICI Direct's research report on KEC International
KEC’s Q2FY20 revenues came in at Rs 2808.8 crore, up 16.6% YoY, above our estimate of Rs 2700 crore. Overall T&D business registered robust growth of 30.3% to Rs 1978 crore, the railways business continued its growth momentum with revenues up 34.7% to Rs 568 crore. Cable business de-grew 5.9% to Rs 249 crore. On the other hand civil & solar business registered weaker performance. EBITDA margins came in at 10.5% (vs. our estimate of 10.4%), which remained stable YoY. During Q2FY20, KEC’s order inflow came in at Rs 2651 crore while the YTD order book was at Rs 18085 crore.
Outlook
We continue to value the stock at 11.5x FY21E EPS and maintain our BUY rating (target price of Rs 335) as risk reward is favourable at this juncture.
For all recommendations report, click here
Disclaimer: The views and investment tips expressed by investment experts/broking houses/rating agencies on moneycontrol.com are their own, and not that of the website or its management. Moneycontrol.com advises users to check with certified experts before taking any investment decisions.
Get access to India's fastest growing financial subscriptions service Moneycontrol Pro for as little as Rs 599 for first year. Use the code "GETPRO". Moneycontrol Pro offers you all the information you need for wealth creation including actionable investment ideas, independent research and insights & analysis For more information, check out the Moneycontrol website or mobile app.


Class WxRobot_Admin
WxRobot_Admin 后台控制类
public static
WxRobot
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
|
public
|
public static
|
$_instance
WxRobot Admin Instance |
#
null
|


Work or social networks
Count of displaying rows:
5
10
15
20
25
Firebase Account
.firebaseapp.com
Please setup firebase


Download Davido’s Latest Album – A Good Time
by Oladipo AbiolaDavido Releases New Album
Super star Nigerian singer and entertainer, David Adeleke, popularly known as Davido on Friday, November 22 dropped a brand new music album.
The Afro-pop star released the album titled ‘A GOOD TIME’ to much jubilation from his fans.
Naija News reports the album has seventeen tracks, with ten of the songs being solo works of Davido, while the other seven featured collaboration with other local and international artists.
Some of those featured in the new album include Popcaan, Peruzzi, Zlatan, Naira Marley, WurlD, Summer Walker, Chris Brown, Gunna, Dremo, A Boogie Wit Da Hoodie and Yonda.
Music producers that worked on the album include Shizzi, Kiddominant, Speroach Beatz, Tekno, etc.
Davido has made the song available on multiple music streaming platforms. Click this link to access the song.


EasyMargining
What is EasyMargining
Sign in
or
Sign up
Simulate, Understand and Optimize
your margin
Free Beta
Margin Comparison
Compare OTC and ETD margin requirements across multiple CCPS.
Understand your margin
Advanced drill-down to understand what is driving your margin.
Optimize your margin
Perform what-if simulations and cheapest-to-clear analysis.


XMLHttpRequest with Struts Example Webapp
Home Page
Welcome! This webapp, which is attached to the article "Ajax using
XMLHttpRequest and Struts" by Frank W. Zammetti, demonstrates a number of
different usages of the XMLHttpRequest object in the Ajax model.
All of these examples have been tested in IE 6.0 and Firefox 1.0. They SHOULD
work in any version subsequent to those, as well as Mozilla 1.0 and higher,
Netscape 7 and higher, Safari 1.2 and higher and Opera 7.60 and higher.
Click the link of the example you would like to see:
Example 1. Simple URL retrieval and display.
This example allows the user to enter a URL, which is then retrieved and
displayed in a <span>. This demonstrates that what is retrieved is NOT
treated as an HTML document but rather a simple stream of data, hence the
document is not parsed and page events don't fire, images don't get retrieved,
etc. This example does NOT really use Struts, everything needed is in
example1.htm.
Example 2. Dynamic table.
This example shows how a table can be built and displayed on-the-fly by showing
sorting of a table based on clicks on the table headers.
Example 3. Dynamic <select> elements.
This example shows how the contents of a <select> can be changed
on-the-fly in response to the change in another.
Example 4. Dynamic <textarea>
This example shows how a <textarea> can be loaded on-the-fly with
content. Here is is one of three poems loaded based on a change in a group
of radio buttons. Also note that unlike example 2 and 3 which renders the
response directly in the Action, this one actually forwards to a JSP that is
the response, and hence no Action is involved.
Example 5. Data submission.
This example shows how XMLHttpRequest can be used to submit data. In this
example, a series of user inputs are used to create a simple CSV string,
which is then submitted to an Action, the values placed in a Value Object, and
that object passed back to the view for display.
Example 6. RSS example.
This example is a more real-world example. It retrieves an RSS feed from one
of three user-selected sources, parses the feed and displays the headlines
in clickable form. This demonstrates retrieving XML from a server and
dealing with it on the client.
Example 7. RSS example #2.
This version of the RSS feed example allows you to enter the URL of
an actual RSS feed. Otherwise it works the same as the other example.
This one uses an Action to retrieve the XML, therebye getting around
the cross-domain issues of the other example.


Welcome to Tommy Willen's JavaScript Quiz!
Once you press start the timer will begin. You will have 75 seconds to answer as many questions as you can. Every correct answer will add 5 points to your score, while every wrong answer will deduct 10 seconds from the timer. The games ends once either you finish all questions or the timer runs out. Your score will be based on the number of correct answers and how long it took you to finish. You score will be added to the high scores page. You can play multiple times to try and beat your top score!. Good Luck!


%- include('parcials/head.html') %>
<%- include('parcials/navigation.html') %>The API reference documentation provides detailed information about a function or object in Node.js. This documentation indicates what arguments a method accepts, the return value of that method, and what errors may be related to that method. It also indicates which methods are available for different versions of Node.js.


Python is an interpreted, high-level and general-purpose, dynamically typed programming language
It is also Object oriented, modular oriented and a scripting language.
In Python, everything is considered as an Object.
A python file has an extension of .py
Python follows Indentation to separate code blocks instead of flower brackets({}).
We can run a python file by the following command in cmd(Windows) or shell(mac/linux).
python <filename.py>
| Data Type | Description |
|---|---|
| int | Integer values [0, 1, -2, 3] |
| float | Floating point values [0.1, 4.532, -5.092] |
| char | Characters [a, b, @, !, ] | | str | Strings [abc, AbC, A@B, sd!, asa] |
| bool | Boolean Values [True, False] |
| char | Characters [a, b, @, !, `] |
| complex | Complex numbers [2+3j, 4-1j] |
| Keyword | Description |
|---|---|
| break | used to exit loop and used to exit |
| char | basic declaration of a type character |
| const | prefix declaration meaning variable can not be changed |
| continue | go to bottom of loop in for, while loops |
| class | to define a class |
| def | to define a function |
| elif | shortcut for (else if) used in else if ladder |
| else | executable statement, part of “if” structure |
| float | basic declaration of floating point |
| for | executable statement, for loop |
| from | executable statement, used to import only specific objects from a package |
| if | executable statement |
| import | to import modules |
| pass | keyword to specify noting is happening in the codeblock, generally used in classes |
| return | executable statement with or without a value |
| while | executable statement, while loop |
| Operator | Description |
|---|---|
| ( ) | grouping parenthesis, function call, tuple declaration |
| [ ] | array indexing, also declaring lists etc. |
| ! | relational not, complement, ! a yields true or false |
| ~ | bitwise not, ones complement, ~a |
| - | unary minus, - a |
| + | unary plus, + a |
| * | multiply, a * b |
| / | divide, a / b |
| % | modulo, a % b |
| + | add, a + b |
| - | subtract, a - b |
| << | shift left, left operand is shifted left by right operand bits |
| >> | shift right, left operand is shifted right by right operand bits |
| < | less than, result is true or false, a %lt; b |
| <= | less than or equal, result is true or false, a <= b |
| > | greater than, result is true or false, a > b |
| >= | greater than or equal, result is true or false, a >= b |
| == | equal, result is true or false, a == b |
| != | not equal, result is true or false, a != b |
| & | bitwise and, a & b |
| ^ | bitwise exclusive or XOR, a ^ b |
| | | bitwise or, a |
| &&, and | relational and, result is true or false, a < b && c >= d |
| ||, or | relational or, result is true or false, a < b || c >= d |
| = | store or assignment |
| += | add and store |
| -= | subtract and store |
| *= | multiply and store |
| /= | divide and store |
| %= | modulo and store |
| <<= | shift left and store |
| >>= | shift right and store |
| &= | bitwise and and store |
| ^= | bitwise exclusive or and store |
| |= | bitwise or and store |
| , | separator as in ( y=x,z=++x ) |
List is a collection which is ordered and changeable. Allows duplicate members.
Lists are created using square brackets:
List items are ordered, changeable, and allow duplicate values.
List items are indexed, the first item has index [0]
, the second item has index [1]
etc.
The list is changeable, meaning that we can change, add, and remove items in a list after it has been created.
To determine how many items a list has, use the len()
function.
[0]
, the second item has index [1]
etc.When we say that tuples are ordered, it means that the items have a defined order, and that order will not change.
len()
function:thistuple = ("apple",)
print(type(thistuple))
#NOT a tuple
thistuple = ("apple")
print(type(thistuple))
Unordered means that the items in a set do not have a defined order.
Set items can appear in a different order every time you use them, and cannot be referred to by index or key.
len()
method.set1 = {"apple", "banana", "cherry"}
set2 = {1, 5, 7, 9, 3}
set3 = {True, False, False}
set4 = {"abc", 34, True, 40, "male"}
set()
constructor to make a set.len()
function.Python has two primitive loop commands: 1. while loops 2. for loops
while
loop we can execute a set of statements as long as a condition is true.break
statement we can stop the loop even if the while condition is trueWith the continue statement we can stop the current iteration, and continue with the next.
With the else statement we can run a block of code once when the condition no longer is true.
A for loop is used for iterating over a sequence (that is either a list, a tuple, a dictionary, a set, or a string).
This is less like the for keyword in other programming languages, and works more like an iterator method as found in other object-orientated programming languages.
The else keyword in a for loop specifies a block of code to be executed when the loop is finished. A nested loop is a loop inside a loop.
The “inner loop” will be executed one time for each iteration of the “outer loop”:
adj = ["red", "big", "tasty"]
fruits = ["apple", "banana", "cherry"]
for x in adj:
for y in fruits:
print(x, y)
None


I’m a full-stack JavaScript engineer and I’m passionate about development across the entire stack from modern front-ends built with React and ES6/7 to Node.js backends and data layers such as MongoDB.


Control! I know exactly what will go in it and how it will look! unfortunately that's intimately tied to the challenges as well!
I have to decide on everything including the colors, the sizing... the look... argh... I super appreciate what talented website designers can do for you!!!
Today, I asked a very good friend (ios guy) the ultimate question: How do you know when you have become a good programmer? The answer was not single worded but I extrapolated ONE WORD out of the conversation. The code would look "Beautiful."
Beautiful code means that an average programmer can easily read and understand the intent of the code with minimal effort.
So now, I should have THREE goals in my life. First, to be beautiful inside. Second, to be beautiful outside. Third, to create code that's beautiful too. Sounds like a journey already happening. Wish me luck!


Simple sample
Are you sure to hit Yes botton?
Two step sample!
Are you sure to go to next step?
This is the second step!
This is the second step. That's all.
Without end step!
The modal will disappear when you hit Yes button.


Mexican president praises Bolivian leader's call for new elections
MEXICO CITY (Reuters) - Mexican President Andres Manuel Lopez Obrador on Sunday praised Bolivian President Evo Morales’ decision to call for new elections, calling it a “triumph of democracy.”
“There are always attitudes, there is an authoritarian eagerness to want to resolve things with confrontation, with violence,” Lopez Obrador said in a video posted to his Twitter page. “The best method to resolve differences is the democratic one.”
Morales said on Sunday he would call for new elections after the Organization of American States (OAS) released a report saying that a disputed Oct. 20 vote should be annulled due to irregularities.


Move your mouse on the screen to move your character.
Eject Mass by Pressing 'W' on your keyboard.
Split yourself into smaller pieces by pressing the 'Spacebar'
Eat pellets and other players in order to grow your cell (food respawns every time a player eats it).
A player's mass is the number of pellets eaten pus the total mass of other players eaten.
Objective: Try to become the biggest cell in the game.
Settings
These are the settings for the game. To view/edit your account settings, please go t your Account Page. Check the box next to the setting you wish to enable. To disable a setting, uncheck the box.


|
Assimp
5.0.1, master @ cd42b955
|
Represents a delay-parsed FBX objects. More...
Public Member Functions | |
| LazyObject (uint64_t id, const Element &element, const Document &doc) | |
| ~LazyObject () | |
| bool | FailedToConstruct () const |
| const Object * | Get (bool dieOnError=false) |
| template<typename T > | |
| const T * | Get (bool dieOnError=false) |
| const Document & | GetDocument () const |
| const Element & | GetElement () const |
| uint64_t | ID () const |
| bool | IsBeingConstructed () const |
Private Types | |
| enum | Flags { BEING_CONSTRUCTED = 0x1 , FAILED_TO_CONSTRUCT = 0x2 } |
Private Attributes | |
| const Document & | doc |
| const Element & | element |
| unsigned int | flags |
| const uint64_t | id |
| std::unique_ptr< const Object > | object |
Represents a delay-parsed FBX objects.
Many objects in the scene are not needed by assimp, so it makes no sense to parse them upfront.
|
private |
| Assimp::FBX::LazyObject::~LazyObject | ( | ) |
|
inline |
| const Object * Assimp::FBX::LazyObject::Get | ( | bool | dieOnError = false | ) |
|
inline |
|
inline |
|
inline |
|
inline |
|
inline |
|
private |
|
private |
|
private |
|
private |
|
private |


JS file downloader
Enter URLS
Add URL
Zip And Download
download
Note: External endpoint request verify CORS


05. Elementary Data Types in Solidity
# Elementary Data Types in Solidity
Similar to other languages, Solidity has elementary and complex data types. Let's first focus on the elementary data types:
- uint
- int
- bool
- address
C4L3 Concept 5 Basic Data Types V2
#### Definitions
- uint - Use for unsigned integers of various sizes
- int - Use for signed integers of various sizes
- bool - The possible values are constants true and false
- address - Represents address of externally owned account or contract account. Holds a 20 byte hex string of an Ethereum address. It is a Value Type.
# Resources
Solidity Documentation
See the full list of Types in the Solidity Types documentation.
Github Source Code Link
SOLUTION:
Yes1
SOLUTION:
82
SOLUTION:
int3


Delete
Are you sure you want to delete this article?
Publish
The article/note will be public. Your followers will be notified when you publish.
UnlistedThe article/note will only be visible to those with the link. It won't be listed on JIJITANG's public pages (your profile, homepage) and people won't be able to find it using a search engine.
Save Draft
Type More of Your Text, Select Text to Add its Format and Add Link....


YelpCamp is a campground review applcation inspired by Yelp.ca. YelpCamp's client side is built using HTML, CSS, javascript and bootstrap's HTML/CSS framework. Users post information about campsites they visited and other Yelpcamp users can comment adding a review or question about the site.
Backend technologies used on Yelpcamp include Node.js, express and MongoDB. Embedded javascript is used in the HTML to create dynamic templates. Yelpcamp has a responsive layout achieved using the implementation of bootstrap.


Remote Table Demo
Simple table widget using AJAX features to fetch and delete data without page reload. You can interactively choose from all common request methods (XMLHTML, Forms, Script).
Please see the
table tutorial
for details.
j2js runtime config
Request Type
XMLHttpRequest
FormHttpRequest
ScriptHttpRequest
Operations
Create
Fetch
Add Row
Remove
Clear


Today on-line selling is difficult, particularly with numerous new Networks and offers showing daily. At 24Scan we attempt to be the simplest and supply the simplest service for our affiliates. we have a tendency to live for Service, we offer Quality, we have a tendency to care for Ethics, we have a tendency to drive Performance, we have a tendency to ar innovators and that we leverage Technology. Get into the action currently and receive consistent money earnings.
Whether you’re an Advertiser or a Publisher, you’ll have a dedicated Account Manager who will be available on a daily basis to review your performance, optimize your current campaigns, plan and implement future projects, and to generally act as a sounding board for any ideas or feedback you may have
we Use offer18 Tracking paltfrom who is very responsive on Mobile and Dextopn Also . instant Count Lead and sell. Give u every report About your tracking link .
Your Account Managers will be constantly assessing all-important metrics like conversion rate, retention rate, and ad velocity to nip fraud in the bud before it even has a chance to materially impact 24scan members.
Happy Affiliate
Top offers
Happy Advertiser
Travels
We are Try to provide Best Service to Our Affiliate For Highly Payout Offers and Weekly payments
We send payment in right time so that you can run can run your business with non-stop. Get your payment via paypal, Payoneer & bitcoin in Every Week .
Expound the actual teachings of the great explorer of the truth, the master-builder of human happiness.
Expound the actual teachings of the great explorer of the truth, the master-builder of human happiness.
Expound the actual teachings of the great explorer of the truth, the master-builder of human happiness.
We Give u Our Best Support on Skype and Email . we Can also Connect With us On live chat Right corner on Our website
We are Collecting Top Converting and Offers in this Industry and Give our Affiliate Best Payout
24scan Have High Convertingcampaigns accessible for the frequent campaign. however one factor promotion solely rely upon your quality traffics. as a result of we have a tendency to guarantee your demand in such approach that you just be. we've well-liked and high gravity campaigns to maximise your affiliate earning. Affiliate manager continually be at your bit and can facilitate to create a winning campaign. we have a tendency to like to see your success.
Our CPA Affiliate Network has thousands of experienced publishers provide quality and performance to your ad campaigns. Our performance- based advertising solutions will ensure that advertisers are maximizing their ROI.Our Affiliates deliver quality conversions, ensuring that our advertisers are only paying for the highest quality traffic to their products and are provided with complete transparency.we Provide Quick traffic as soon as Possible With No Setup Cost With 24/7 Support
Phone : +1(213)262-2360
Mobile : +1(213)262-2360
<EMAIL_ADDRESS>www.24scan.com
Skype: Support@24scan.com


Try Again
Download Report (test)
Provide Feedback
Score
Report Result
Naming Checks
Usage Checks
Documentation Checks
Project Desciption
{{folderStructure|safe}}
Try Again
Provide Feedback


We're sorry but webpage doesn't work properly without JavaScript enabled. Please enable it to continue.


Up to now, I have had four periods of internships related to content creation and operation, and more than 40 works have been published, including H5, electronic papers, and tweet articles.
As these may benefit me at content operation, so I would like to introduce some of my works, please check and have a look?


Financial Information
Upload the following documents
- Balance sheet
- Profit and loss statement
- Cash flows
- Notes to the financial statements describing related party transactions, debt agreements, commitments, and other material
agreement
- Income tax submissions
- Notice of Assessment or the equivalent document from the relevant jurisdiction.


Beware what you post, all the forms send data to https://example.com.
Credit Card autofill
Name on card:
Credit card number:
Expiry Date:
01
02
03
04
05
06
07
08
09
10
11
12
/
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
CVC:
CC Fill Form (Simpsons)
CC Fill Form (Superman)
CC Fill Form (President)
submit