Stutter is built with Javascript in mind but is heavily inspired by Clojure. Stutter aims to make functional programming easier by introducing support for functional concepts while still maintaining direct compatibility with native javascript.
There are some conceptual differences that you should be aware of while working
with stutter
Properties and Keys are two different concepts within stutter. Often these are conflated in some other libraries. We have separated them out to help keep things consistent and to more easily reason about the two concepts.
JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name and a value.
These are easily accessed using the dot or bracket notation in Javascript
const object = {
foo: 'bar'
}
// foo is the property
object.foo
//=> 'bar'
object['foo']
In Javascript, all properties are strings. Even when accessing a property using a non string value, the value is first cast as a string before accessing the property's value.
const object = {
'1': 'a'
}
object[1]
//=> 'a'
The above example works because the number is first typecast to a string before accessing the value.
When using property based methods in stutter (such as getProperty
),
we consider only strings to be Properties.
Keys in javascript are used by such data structures as Map and WeakMap for storing and accessing values using the given Key. These are different from
Properties, keys and indexes are commonly used in javascript for storing and accessing values. It is also common to iterate over these values. For this reason, we have introduced
In javascript the following values are false
false
null
undefined
''
0
NaN
When performing falsy checks in stutter
the following Javascript values are considered
false
false
null
undefined
NaN
TODO BRN: Move this section into the METHODS_BY_COMPARISON.md file
javascript
1 + 2 + 3
// => 6
clojure
(+ 1 2 3)
;; => 6
stutter
add(1, 2, 3)
// => 6
javascript
if (true) {
return "By Zeus's hammer!"
} else {
return "By Aquaman's trident!"
}
;; => "By Zeus's hammer!"
clojure
(if true
"By Zeus's hammer!"
"By Aquaman's trident!")
;; => "By Zeus's hammer!"
stutter
import $ from 'stutter'
$.if(true,
"By Zeus's hammer!",
"By Aquaman's trident!")
Prints a value to standard out.
javascript
process.stdout.write('foo')
// => 'foo'
clojure
(print "foo")
; > foo
stutter
In javascript this will fallback to console.log
if process.stdout.write
is not available
print('foo')
Same as print with a new line \n
added to the end
clojure
(println "foo")
; > "foo"
stutter
In javascript this will fallback to console.log
if process.stdout.write
is not available
println("foo")
// > 'foo\n'
Evaluates multiple expressions and returns the last value. Returns null
if no expressions are supplied.
clojure
(do (print "Success!")
"By Zeus's hammer!")
stutter
$.do($(print, "Success!")
"By Zeus's hammer!")
clojure
(when true
(println "Success!")
"abra cadabra")
stutter
when(true,
$(println, "Success!"),
"abra cadabra")
clojure
(nil? 1) ; => false
(nil? nil) ; => true
stutter
$(nil, 1) // => false
$nil(1) // => false
nil(1) // false
$(nil, null) // => true
$nil(null) // => true
nil(null) // true
$(nil, undefined) // => true
$nil(undefined) // => true
nil(undefined) // true
Equality operator
clojure
(= 1 1) ; => true
(= nil nil) ; => true
(= 1 2) ; => false
stutter
$(eq, 1, 1) // => true
$eq(1, 1) // => true
eq(1, 1) // true
$(eq, null, null) // => true
$eq(null, null) // => true
eq(null, null) // true
$(eq, 1, 2) // => false
$eq(1, 2) // => false
eq(1, 2) // false
Evaluates expressions one at a time, from left to right. Returns the first value that evaluates to a logical true value, otherwise it returns the value of the last expression. If no expressions exist it returns null
. Expressions after the first true
are not evaluated. Expressions are not coerced to a boolean, instead their value is returned.
clojure
(or nil nil) ; nil
(or false nil) ; nil
(or true nil) ; true
;; or doesn't evaluate if the first value is true
(or true (println "foo")) ; true
;; order matters
(or (println "foo") true) ; 'foo' true
;; does not coerce a given value to a boolean true, returns the value
(or false 42) ; 42
(or false 42 9999) ; 42
(or 42 9999) ; 42
stutter
or(null, null) // => null
or(false, null) // => null
or(true, nil) // => true
// or doesn't evaluate if the first value is true
or(true, println("foo")) // => true
// order matters
or(println("foo"), true) // => > 'foo' true
// does not coerce a given value to a boolean true, returns the value
or(false, 42) // => 42
or(false, 42, 9999) // => 42
or(42, 9999) // => 42
Evaluates expressions one at a time, from left to right. Returns the first value that evaluates to a logical false
value, otherwise it returns the value of the last expression. If no expressions exist it returns null
. Expressions after the first false
are not evaluated. Expressions are not coerced to a boolean, instead their value is returned.
clojure
(and true true) ; true
(and true false) ; false
(and null false) ; null
stutter
and(true, true) // true
and(true, false) // false
and(null, false) // null
Creates a global var in the current namespace with the given name.
clojure
(def myVal 5)
stutter
def('myVal', 5)
Thread first macro
clojure
(-> person
(assoc :hair-color :gray)
(update :age inc))
stutter
_(person)
.assoc('.hairColor', 'gray')
.update('.age', inc)
_(person,
assoc('.hairColor', 'gray'),
update('.age', inc))
$._(person,
assoc('.hairColor', 'gray'),
update('.age', inc))
Thread last macro
clojure
(->> (range 10)
(filter odd?)
(map #(* % %))
(reduce +)))
stutter
_last(range(10)
.filter(odd)
map(pow(_, 2))
NOTE: Thread gets run before every invocation of the next function
clojure
(as-> [:foo :bar] v
(map name v)
(first v)
(.substring v 1))
stutter
stutter.as()._()