& Things I wish Hack Reactor Had Taught Me Part II
Sr. Front End Engineer, Deverus
HackReactor Austin (nee MakerSquare) Cohort #21
brian.boyko@gmail.com
Brian Boyko
This stuff can be powerful.
It can be clever.
But if you're working with developers not used to these ideas, stick with the classics.
0x080483e4
0x080483f0
0x080483f2
0x80484e03
Typically used in "lower level" languages like C and C++.
Pointers are memory address values.
Imagine all your computer memory is accessible from a single array. The pointer is the index value.
It's called a "pointer" because you usually use it to "point" to the memory address you want the computer to access.
dun
dun
dun
(yes, but you don't have to worry about it.)
// LANGUAGE: C
int getNextRandomValue(void){
return rand(); // C's version of Math.random();
}
void populate_array(
int *array,
size_t arraySize,
int (*somePointerToAFunction)(void) // what's going on here?
){
for (size_t i=0; i < arraySize; i++)
array[i] = somePointerToAFunction();
}
int main(void){
int myarray[10];
populate_array(myarray, 10, &getNextRandomValue);
}
Unlike Javascript, C doesn't have
higher order functions.
You can't pass in a function as a parameter. Only ints, chars, and a few other primative types.
But... in C:
// LANGUAGE: JAVASCRIPT ES6
const getNextRandomValue = () => Math.random()
const populateArray = (arr, arrSize, callbackRandom) => {
for(let i = 0; i < arrSize, i++){
arr.push(callbackRandom());
}
}
const main = () => {
populateArray([], 10, getNextRandomValue);
}
The exact same thing.
The computer isn't smart enough to know what a function is. But Javascript abstracts that information for you when you need it.
Whenever you "pass by reference" instead of "pass by value," what you're really doing is passing the value of a pointer.
ain't.
That value cannot be reassigned.
// LANGUAGE: JAVASCRIPT ES6
var a = 1;
console.log(a); //=> 1
a = 2;
console.log(a); //=> 2
let b = 1;
console.log(b); //=> 1
b = 2;
console.log(b); //=> 2
const c = 1;
console.log(c); //=> 1
c = 2
//=> Uncaught TypeError: Assignment to constant variable.
let a = "foo" // a is assigned to "foo"
let b = { bar: "foo" } // b is assigned to the object?
const c = {bar: "foo"} // well, if that's true...
// then c should ALWAYS equal {bar: "foo"}
// LANGUAGE: NODE REPL
$ node
> const c = {bar: "foo"}
undefined
> c
{ bar: 'foo' }
> c = {bar: "unfoo"}
TypeError: Assignment to constant variable.
at repl:1:3
at ContextifyScript.Script.runInThisContext (vm.js:44:33)
at REPLServer.defaultEval (repl.js:239:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:433:10)
at emitOne (events.js:120:20)
at REPLServer.emit (events.js:210:7)
at REPLServer.Interface._onLine (readline.js:278:10)
at REPLServer.Interface._line (readline.js:625:8)
> c.bar = "unfoo"
'unfoo'
> c
{ bar: 'unfoo' }
>
You can't change a variable declared with const.
But if that variable is really just a pointer, pointing to a reference, you can modify the value at that reference.
Object.freeze(obj) is part of the JS language.
> c
{ bar: 'unfoo' }
> Object.freeze(c)
{ bar: 'unfoo' }
> c
{ bar: 'unfoo' }
> c.bar = "refoo"
'refoo'
> c
{ bar: 'unfoo' }
>
> const d = {baz: {bang: "biz"}}
undefined
> d
{ baz: { bang: 'biz' } }
> Object.freeze(d)
{ baz: { bang: 'biz' } }
> d.baz
{ bang: 'biz' }
> d.baz = "woo"
'woo'
> d.baz
{ bang: 'biz' }
> d.baz.bang = "innerwoo"
'innerwoo'
> d.baz.bang
'innerwoo'
> d
{ baz: { bang: 'innerwoo' } }
>
This is why immutable.js is a thing.
To fix this, you'd have to recursively freeze all objects inside of an object.
You may end up using WebAssembly, or not. But understanding pointers, whether or not you use them, makes you a stronger dev.
here's one way to do it.
export const getFromServer = (endpoint, token, queryParameters) =>
new Promise((resolve, reject) => {
let url = makeURL(endpoint, token); // just creates the URL string.
request
.get(url)
.query(queryParams)
.then(res => resolve(res.body))
.catch((err, res) => reject({ error: true, msg: err, res }));
});
Take this function. Please.
You *could* remember that every time you invoke this function, you need to remember the right endpoints and the right token... or.....
export const getFromServer = (endpoint, token, queryParameters) =>
new Promise((resolve, reject) => {
let url = makeURL(endpoint, token); // just creates the URL string.
request
.get(url)
.query(queryParams)
.then(res => resolve(res.body))
.catch((err, res) => reject({ error: true, msg: err, res }));
});
const curriedGet = (token) =>
(endpoint) =>
(queryParameters) =>
getFromServer(endpoint, token, queryParameters);
export const makeAPI = (token) => ({
users: curriedGet(token)("/users"),
blogs: curriedGet(token)("/blogs"),
pictures: curriedGet(token)("/pictures")
})
// in the code.
import TOKEN from '../constants/token.js';
import { makeAPI } from '../api'
const api = makeAPI(TOKEN);
api.users({user: "Phil"})
.then((data) => console.log(data))
api.blogs({id: 32830})
.then((data) => console.log(data))
api.pictures()
.then((data) => console.log(data))
it helps with
"same kinda thing,
different configuration" problems.
const fbGenerator = (fizzer, buzzer) =>
(start, end) => {
let output = [];
for (let i = start; i < end; i++) {
if (i % fizzer === 0 && i % buzzer === 0) {
output.push("Fizzbuzz");
} else if (i % buzzer === 0) {
output.push("Buzz");
} else if (i % fizzer === 0) {
output.push("Fizz");
} else {
output.push(i);
}
}
return output;
};
const classicFizzbuzz = fbGenerator(3, 5);
classicFizzbuzz(1, 20)
// => [1,2,"Fizz",4,"Buzz","Fizz",7,8,"Fizz",
// "Buzz",11,"Fizz",13,14,"Fizzbuzz",16,17,"Fizz",19]
const funkyFizzbuzz = fbGenerator(4, 7)
funkyFizzbuzz(1, 20)
// => [1,2,3,"Fizz",5,6,"Buzz","Fizz",9,10,11,
// "Fizz",13,"Buzz",15,"Fizz",17,18,19]
// and yes, you can skip a step.
fbGenerator(3, 5)(1, 10)
// => [ 1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz' ]
so, why curry?
so, when curry?
It should be noted that no ethically-trained software engineer would ever consent to write a nukeLasVegas() function.
-- quote modified from Nathaniel Borenstein
Basic professional ethics would instead require the creation of a nukeCity() function, to which Las Vegas can be provided as a parameter.
let armNuke = (target) => () => nuke(target);
let redButton = {
lasVegas: armNuke("Las Vegas"),
disneyWorld: armNuke("Disney World"),
guam: armNuke("Guam"),
}
simple. delicious.
const add = (a, b) => (a + b);
// ES6+
const curry = (f, ...curriedArgs) =>
(...newArgs) =>
f.apply(null, curriedArgs.concat(newArgs));
const addSeven = curry(add, 7)
// => (b) => 7 + b;
addSeven(3); // => 10;
oh, and one last thing:
some programming languages, including some "hot" ones like Elm, and Haskell, use currying to handle *any* function with more than one parameters.
-- ELM language
addfun: Int -> Int -> Int -> Int
addfun x y z = x + y + z
partiallyapplied1 = addfun 2
-- partiallyapplied1 = y z = 2 + y + z
google "function arity"
if for no other reason
than to sound smart.
Hey, you know what has .map()? Arrays.
console.log([1, 2, 3].map((x) => x * x)) //=> [1, 4, 9]
but what else can we map?
// object oriented
const curry = (f, ...curriedArgs) =>
(...newArgs) =>
f.apply(null, curriedArgs.concat(newArgs));
class Wrapper {
constructor(value){
this.value = value;
}
map(f){
return new Wrapper(f(this.value))
}
}
const something = new Wrapper(39)
console.log(something);
// => { value: 39 }
const add = (a, b) => a + b;
const addThree = curry(add, 3);
const somethingPlusThree =
something.map(addThree)
console.log(somethingPlusThree)
// => { value: 42 }
// little more functional,
// but functionally similar.
const Identity = (value) => ({
value: value,
map: (f) => Identity(f(value))
})
const square = x => x * x;
const idTwo = Identity(2);
console.log(idTwo)
// => Object {value: 2, map: function}
console.log(idTwo.value)
// => 2
console.log(idTwo
.map(square).value)
// => 4
console.log(idTwo
.map(square)
.map(square)
.map(square)
.map(square)
.value)
// => 65536
class MappableDictionary {
constructor(obj){
this.value = obj;
}
map(f) {
return new MappableDictionary(
Object.keys(this.value)
.reduce((pv, key) =>
Object.assign(pv, {
[key]: f(this.value[key])
}), {}))
}
}
let namegame = new MappableDictionary({
Adam: "Adam",
Jill: "Jill",
Chuck: "Chuck",
})
const letsDoB = (st) => st + " " +
st + " Bo-B" + st.substring(1);
const firstVerse = namegame.map(letsDoB);
console.log(firstVerse.value)
//{
// Adam: "Adam Adam Bo-Bdam",
// Jill: "Jill Jill Bo-Bill",
// Chuck: "Chuck Chuck Bo-Bhuck",
//}
const MappableObject = (obj) => ({
value: obj,
map: (f) => MappableObject(Object.keys(obj)
.reduce((pv, key) =>
Object.assign(pv, {
[key]: f(obj[key])
}), {}))
})
const sorter = (arr) => arr.sort();
const stuff = {
fruit: ['cherries', 'apples', 'bananas'],
numbers: [3, 7, 2],
loneString: "I'm so lonely"
}
let MappableStuff = MappableObject(stuff);
console.log(MappableStuff
.map((x) => Array.isArray(x)
? sorter(x)
: x)
.value)
// {
// fruit: ["apples", "bananas", "cherries"],
// numbers: [2, 3, 7],
// loneString: "I'm so lonely"
// {
How is this useful?
let num = 8
const addTwo = (x) => x + 2;
const double = (x) => x * 2;
const square = (x) => x * x;
const sqRoot = (x) => Math.pow(x, 0.5);
const subThree = (x) => x - 3;
let answer1 = addTwo(double(square(subThree(sqRoot(addTwo(double(square(num))))))));
//=> 143.17894898810343
This works. And is probably how you'd do it in most situations.
But isn't it kind of awkward?
It's kinda like... you're inside out.
What is 8 squared, times two, plus two, to the 0.5th power(i.e., square root), minus three, times two?
The code looks an awful lot like our english language problem. There's no real nesting of functions, so it might be easier to reason about.
What is 8 squared, times two, plus two, to the 0.5th power(i.e., square root), minus three, times two?
let answer2 = Identity(num)
.map(square)
.map(double)
.map(addTwo)
.map(sqRoot)
.map(subThree)
.map(square)
.map(double)
.value; //=> 143.17894898810343
const Identity = (value) => ({
value: value,
map: (f) => Identity(f(value))
})
let num = 8
const addTwo = (x) => x + 2;
const double = (x) => x * 2;
const square = (x) => x * x;
const sqRoot = (x) => Math.pow(x, 0.5);
const subThree = (x) => x - 3;
The tradeoff:
Novice developers probably won't get this right away. Even experienced developers unfamiliar with the pattern might not.
Now, Identity is a very *specific* use case of a monad. You can expand it a little bit to get this. And there's other monads too...
const Just = (value) => ({
inspect: (comment) => {
console.log(`Value ${value}`, comment)
return Just(value)
},
chain: (f) => f(value),
map: (f) => Just(f(value)),
applyMonad: (m) => m.map(value),
})
// how can we get the value?
// we could add a "value" method...
// or...
const identity = x => x;
Just("foo").chain(identity) // "foo"
const Identity = (value) => ({
value: value,
map: (f) => Identity(f(value))
})
Remember: A monad is just a monoid in the category of endofunctors
Don't worry, I don't know what that means either. A Monad's just a data structure, like linked lists or queues.
Check out "Functional-light JS"
by Kyle Simpson for more:
https://github.com/getify/Functional-Light-JS
const Just = (value) => ({
inspect: (comment) => {
console.log(`Value ${value}`, comment)
return Just(value)
},
chain: (f) => f(value),
map: (f) => Just(f(value)),
applyMonad: (m) => m.map(value),
})
const addTwo = (x) => x + 2;
const double = (x) => x * 2;
const square = (x) => x * x;
const sqRoot = (x) => Math.pow(x, 0.5);
const subThree = (x) => x - 3;
const identity = (x) => x;
Just(8)
.inspect("Initial") // => Value 8 Initial
.map(square)
.inspect("After square()") // > Value 64 After square()
.map(double)
.inspect("After double()") // > Value 128 After double()
.map(addTwo)
.inspect("After addTwo()") // > Value 130 After double()
.map(sqRoot)
.inspect("After sqRoot()") // > Value 11.40175425099138 After sqRoot()
.map(subThree)
.inspect("After subThree()") // > Value 8.40175425099138 After subThree()
.map(square)
.inspect("After square()") // > Value 70.58947449405171 After square()
.map(double)
.inspect("After double()") // > Value 141.17894898810343 After double()
.chain(identity); // => 143.17894898810343
Things I wish Hack Reactor Taught Me Part II
It still holds true that the main difference between a Jr. and Sr. engineer is "how much you need to be mentored" vs. "how much mentoring you can do" but there are other differences.
Essentially, engineering is all about cooperation, collaboration, and empathy for both your colleagues and your customers. If someone told you that engineering was a field where you could get away with not dealing with people or feelings, then I’m very sorry to tell you that you have been lied to.
Solitary work is something that only happens at the most junior levels, and even then it’s only possible because someone senior to you — most likely your manager — has been putting in long hours to build up the social structures in your group that let you focus on code.
-- Yonatan Zunger, former engineer at Google
Most users of web applications are doing so from a mobile device.
If you have limited money, why buy a $1000 computer and $70 broadband/mo connection when you can pay only $70/mo for a phone that you need anyway?
But this presents us with difficulties.
PLUS mobile phones are big in markets that don't use Latin alphabetic characters.
Interactivity on a 3G connection in 5 seconds or less.
There are some *great* libraries out there that contain a ton of functions and components that make your site awesome. But each library you import adds more bloat.
3G can be as slow as 250k/sec
I would recommend using React or Angular for your thesis project, for employability, but consider these for other projects once you graduate
Frameworks
3G can be as slow as 250k/sec
Libraries
Sometimes it pays to "reinvent the wheel" if you only need a part of the wheel.
If you only need a few methods from Lodash/Underscore, consider writing them from scratch yourself instead of importing them from the library.
Typescript & Flow
But...
Typed Javascript: Typescript & Flow
a.m.a.
email: brian.boyko@gmail.com
github: github.com/brianboyko
linkedin: linkedin.com/in/brianboyko