Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Last active August 8, 2017 04:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/bb1c85c2434f7b3af17e6f94860b72f2 to your computer and use it in GitHub Desktop.
Save panesofglass/bb1c85c2434f7b3af17e6f94860b72f2 to your computer and use it in GitHub Desktop.
Iron Yard FizzBuzz
// type for fizzbuzz results
type FizzBuzzResult =
| FizzBuzz
| Fizz
| Buzz
| Number of int
let fizzbuzz n =
match n % 3, n % 5 with
| 0, 0 -> FizzBuzz
| 0, _ -> Fizz
| _, 0 -> Buzz
| s -> Number n
for i in 1..100 do printfn "%A" (fizzbuzz i)
/* function syntax
function fizzbuzzFactory(fizz = 3, fizzText = 'fizz', buzz = 5, buzzText = 'buzz') {
return function (i) {
return i % fizz === 0 && i % buzz === 0 ? fizzText + buzzText
: i % fizz === 0 ? fizzText
: i % buzz === 0 ? buzzText
: i.toString()
}
}
*/
/* const declaration */
const fizzbuzzFactory = (fizz = 3, fizzText = 'fizz', buzz = 5, buzzText = 'buzz') => {
return (i) => {
return i % fizz === 0 && i % buzz === 0 ? fizzText + buzzText
: i % fizz === 0 ? fizzText
: i % buzz === 0 ? buzzText
: i.toString()
}
}
/* Class syntax */
class FizzbuzzFactoryClass {
constructor(private fizz = 3,
private fizzText = 'fizz',
private buzz = 5,
private buzzText = 'buzz') {
}
exec(i) {
return i % this.fizz === 0 && i % this.buzz === 0 ? this.fizzText + this.buzzText
: i % this.fizz === 0 ? this.fizzText
: i % this.buzz === 0 ? this.buzzText
: i.toString()
}
}
const fbFactoryClass =
// (i) => new FizzbuzzFactoryClass().exec(i)
// is the same as
new FizzbuzzFactoryClass().exec
// test
const fizzbuzzTest = fizzbuzzFactory()
console.assert(fizzbuzzTest(3) === 'fizz')
console.assert(fizzbuzzTest(5) === 'buzz')
console.assert(fizzbuzzTest(15) == 'fizzbuzz')
console.assert(fizzbuzzTest(49) !== 'buzz')
// production
const fizzbuzzProd = fizzbuzzFactory(2, 'fuzz', 7, 'bizz')
/*
for (let i = 1; i <= 100; i++) {
console.log(fizzbuzzProd(i))
}
*/
const loopFactory = (start = 1, end = 100, increment = 1) => {
// using recursion
const loop = (acc, x) => {
if (x <= end) {
acc.push(x)
return loop(acc, x + increment)
} else {
return acc
}
}
return loop([], start)
/* using a loop and mutation
const result = []
for (let i = start; i <= end; i += increment) {
result.push(i)
}
return result
*/
}
console.log(loopFactory(1000000, 1010000, 1000))
// Chaining functions
console.log(loopFactory().map(fizzbuzzProd).filter((x) => x === 'fuzz' || x === 'bizz'))
// Test that all % 3 && % 5 values produce a 'fizzbuzz' result
const fizzes =
loopFactory()
.filter((x) => x % 3 === 0 && x % 5 === 0)
.reduce((acc, x) => acc && fizzbuzzTest(x) === 'fizzbuzz', true)
console.assert(fizzes === true)

Here's a good post on lazy iteration using both generators and continuations. Note that continuations are function based, and this can provide a very efficient, lazy approach to iteration. Lazy Iteration with Continuation

// simplest possible function: no-op
function nop() {
}
console.log(nop())
// identity function: the simplest "true" function
function id(a) {
return a
}
console.log(id(1))
console.log(id('hello'))
// identity function using ES6 lambda with a const declaration
const id2 = (a) => a
console.log(id2(1))
console.log(id2('hello'))
// Testing
// Functions are terrific for testing, so long as you keep them "pure".
// What's pure? It means you simply return a value. If you do anything
// else, it's much harder to test the result since whatever you changed
// is both hidden from the caller and potentially also being changed
// by something else. This property is also called "referential integrity".
// If you know you will get the same output for the same input every time,
// your function is trivial to test.
const add = (x, y) => x + y
console.assert(add(1, 1) === 2)
console.assert(add(2, 2) === 4)
//console.assert(add(2, 3) === 4, 'expected add(2,3) === 4, but it wasn\'t')
console.assert(add(1, add(1, 1)) === 3)
// Iteration
// Functions may seem a bit useless if you can't change things
// from within them. However, I am not saying you can never change
// things; only that a function is difficult or impossible to accurately
// test if you do. Let's say we want to make things testable. What will
// we do if we get an array and need to do something with it?
const test = [1,2,3,4]
const out = []
for (let i = 0; i < test.length; i++) {
out.push(test[i] + 1)
}
console.log(out)
// We changed each value by adding one and adding it to a new array.
// JavaScript has many built-in functions for handling this type of
// work without requiring us to modify values in a loop.
const out2 = test.map((x) => x + 1)
console.log(out2)
// In many fewer lines of code, we pushed the loop into a single
// call to Array.prototype.map. Notice what's inside. It's a function.
// This makes `map` a "higher-order function". Any function that takes
// a function as a parameter or returns a function is considered
// higher-order. We'll take a look at the latter in a moment. Let's
// refactor this to extract our heavy-duty logic.
const add1 = (x) => x + 1
const out3 = test.map(add1)
console.log('out3', out3)
// You are seeing that correctly. We could have written:
const out4 = test.map((x) => add1(x))
// or even
const out5 = test.map(function (x) { return add1(x) })
// but why bother?
// Also notice how easy it would be to test our add1 function.
console.assert(add1(1) === 2)
// Our code is getting smaller and more readable, at least in my opinion.
// Array has several other useful functions:
const filtered = test.filter((x) => x < 3)
const reduced = test.reduce((acc, x) => acc + x)
// What do you think these two functions will do?
console.log('filtered', filtered)
console.log('reduced', reduced)
// Higher-order functions
function uncurriedAdd(a, b) {
return a + b
}
console.log(uncurriedAdd(1, 1))
console.log(uncurriedAdd(2, 2))
function curriedAdd(a) {
return function(b) {
return a + b
}
}
console.log(curriedAdd(1)(1))
console.log(curriedAdd(2)(2))
console.log(curriedAdd(1))
const add1 = (b) => curriedAdd(1)(b)
console.log(add1(1))
// TODO: discuss compose (others from JS Allonge?)
const compose = <A, B, C>(c: (b: B) => C, b: (a: A) => B) => (a: A) => c(b(a))
//const compose = (c, b) => (a) => c(b(a))
const addOne = (x) => x + 1
const double = (x) => x * 2
const asString = (x) => x.toString()
const addOneAndDouble = compose(double, addOne)
const addOneAndDoubleString = compose(asString, compose(double, addOne))
console.log(addOneAndDouble(1) === 4)
console.log(addOneAndDoubleString(1) === '4')
const pipeline = (...fns) =>
(value) =>
fns.reduce((acc, fn) => fn(acc), value)
const addOneAndDouble2 = pipeline(addOne, double)
const addOneAndDoubleString2 = pipeline(addOne, double, asString)
console.log(addOneAndDouble2(1) === 4)
console.log(addOneAndDoubleString2(1) === '4')
// Why care about these funny words, curry and uncurry?
// These have many and very useful applications.
// What is a class definition?
class Sample {
a
constructor(a) {
this.a = a
}
equals(b) {
return this.a === b
}
}
const s = new Sample(1)
console.log(s.equals(1))
// A similar, yet simpler implementation using a curried function:
function Sample2(a) {
return function equals(b) {
return a === b
}
}
console.log(Sample2(1)(1))
// But, that's not a class! I can't create an instance!
// Do you really need to? In many cases, you don't need to do so.
// Simply closing over a value is sufficient:
const s2 = Sample2(1)
console.log(s2(0))
console.log(s2(1))
console.log(s2(2))
// You can indeed reuse the instance you create here.
// However, you cannot retrieve values once passed in.
// Is it possible to use this strategy to return more
// than just a simple function?
function Sample3(a) {
function equals(b) {
return a === b
}
function compare(b) {
if (a < b) {
return 1
} else if (a > b) {
return -1
} else {
return 0
}
}
return {
equals,
compare
}
}
const s3 = Sample3(1)
console.log(s3.equals(1))
console.log(s3.compare(0))
console.log(s3.compare(1))
console.log(s3.compare(2))
// We could take this to another level and allow
// the consumer to specify behavior!
function Sample3HO(a, compare) {
function _equals(b) {
return a === b
}
function _compare(b) {
return compare(a, b)
}
return {
equals: _equals,
compare: _compare
}
}
const s3ho = Sample3HO(1, function (a, b) {
// Reverse the comparison
if (a > b) {
return 1
} else if (a < b) {
return -1
} else {
return 0
}
})
console.log(s3ho.equals(1))
console.log(s3ho.compare(0))
console.log(s3ho.compare(1))
console.log(s3ho.compare(2))
// That looks an awful lot like a class!
// Well yes, but the functions are per-instance,
// not shared as they would be in a class.
// What does that mean?
// JavaScript uses prototypal inheritance.
// Class instances all share a common prototype:
function Sample4(a) {
this.a = a
}
Sample4.prototype.equals = function equals(b) {
return this.a === b
}
Sample4.prototype.compare = function compare(b) {
if (this.a < b) {
return 1
} else if (this.a > b) {
return -1
} else {
return 0
}
}
const s4 = new Sample4(1)
console.log(s4.equals(1))
console.log(s4.compare(0))
console.log(s4.compare(1))
console.log(s4.compare(2))
console.log('properties in Sample3')
for (let prop in s3) {
if (s3.hasOwnProperty(prop)) {
console.log(prop)
}
}
console.log('properties in Sample4')
for (let prop in s4) {
if (s4.hasOwnProperty(prop)) {
console.log(prop)
}
}
console.log('properties in Sample4 prototype')
const s4proto = s4.__proto__
for (let prop in s4) {
if (s4proto.hasOwnProperty(prop)) {
console.log(prop)
}
}
console.log('properties in Sample class')
for (let prop in s) {
if (s.hasOwnProperty(prop)) {
console.log(prop)
}
}
console.log('properties in Sample prototype')
// Can't access the __proto__ on a class instance
const sproto = Sample.prototype
for (let prop in s) {
if (sproto.hasOwnProperty(prop)) {
console.log(prop)
}
}
// We can therefore see how classes are constructed.
// What can we do with this, and why would we ever
// care to use this archaic form when the lovely class
// keyword now exists?
// What can we learn from the above?
// 1. We can return a function from another function.
// 2. We can provide functions in object literals.
// 3. We can pass a function as an argument to another function.
// 4. A function can serve as the constructor of a class.
// Factory functions
// Let's combine all of the above to create a factory function:
function createSample() {
function Sample(a) {
this.a = a
}
Sample.prototype.equals = function equals(b) {
return this.a === b
}
return Sample
}
const MySample = createSample()
const s5 = new MySample(1)
console.log(s5.equals(1))
// That seems hardly worth the effort. Let's see what else we could do ...
function createWow(behaviors) {
function Wow(name) {
this.name = name
}
function isFunction(functionToCheck) {
const getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'
}
for (let b in behaviors) {
if (isFunction(behaviors[b])) {
Wow.prototype[b] = behaviors[b]
}
}
return Wow
}
// What do you think this will do?
const Wow = createWow({
speak: function(word) { return word },
add: function (a, b) { return a + b }
})
const wow = new Wow('mom')
console.log(wow.name)
console.log(wow.speak('howdy'))
console.log(wow.add(1, 1))
// How is this any different than what we did above?
console.log('properties of Wow')
for (let prop in wow) {
if (wow.hasOwnProperty(prop)) {
console.log(prop)
}
}
console.log('properties of Wow.prototype')
for (let prop in wow) {
if (wow.__proto__.hasOwnProperty(prop)) {
console.log(prop)
}
}
// This doesn't seem too useful. Why not just define the class
// the way you want it or use `extends` for proper subclasses?
// Great point. But you may see an opportunity for flipping this:
const FunctionalMixin = (behavior) =>
function (target) {
for (let property in behavior) {
if (!target.hasOwnProperty(property)) {
Object.defineProperty(target, property, {
value: behavior[property],
writable: true
})
}
}
return target
}
// Now we can define a mixin that will add compare to our Sample class
// we defined above:
const Comparable = FunctionalMixin({
compare(b) {
if (this.a > b) {
return -1
} else if (this.a < b) {
return 1
} else {
return 0
}
}
})
Comparable(Sample.prototype)
// Unfortunately, TypeScript won't compile this.
//s.compare(1)
console.log('Comparable Sample', (<any>s).compare(1))
// We've added functionality to an existing class by modifying its
// prototype, and an existing value picked up the behavior automatically.
// Is this useful? Sometimes, though you have to be careful with it.
// There are other, even more general approaches to adding behaviors
// that you should investigate, but they are also more complex.
// I'll also add that I don't typically use this style in my
// day-to-day work anymore, though I used to do this a lot.
// I've shown only a handful of things you can do with functions.
// Topics I didn't cover include decorators, variadic functions,
// traits, and many other things that involve a bit more explanation.
// However, I encourage you to explore some of these topics,
// and in particular, check out Reginald Braithwaite's
// JavaScript Allonge, which covers these and many more topics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment