DEV Community

benboorstein
benboorstein

Posted on • Updated on

My Raw Notes for the Week of 12-6-2021

ENGLISH:

Currying:
- fCC paraphrase: The 'arity' of a function is the number of arguments
it requires. 'Currying' a function means to convert a function of N arity
into N functions of arity 1. In other words, currying restructures a
function so that the function takes one argument, then returns another
function that takes the next argument, and so on.
- Gandolfo's definition: "Currying is when you create a function that can
later be called multiple times with different arguments."

Composing:
- Gandolfo's definition: "Composing is, essentially, when you take two
functions and combine them."

Closure:
- From this article https://dmitripavlutin.com/simple-explanation-of-javascript-closures/:
  - "The closure is a function that accesses its lexical scope even [when it's] executed
  outside of its lexical scope."
  - "...the closure is a function that captures variables from outer function(s)."
  - "Finally, a closure is a function that captures variables from its lexical scope. In
  simple words, the closure (which is a function) remembers the variables from the place
  where it (the function (the closure)) is defined, no matter where it is executed."
- From this https://eloquentjavascript.net/03_functions.html:
  - "Thinking about programs like this takes some practice. A good mental model is to think
  of function values as containing both the code in their body and the environment in which
  they are created. When called, the function body sees the environment in which it was
  created, not the environment in which it is called."
- Robert paraphrase:
  - Even though the call stack is cleared after the function runs, any variable still needed
  for reference is "remembered".
Enter fullscreen mode Exit fullscreen mode
CODE:

// JS
// Currying (fCC example 1)
function unCurried(x, y) {
  return x + y
}
console.log(unCurried(1, 2)) // 3

function curried(x) {
  return function(y) {
    return x + y
  }
}
// more concise way: const curried = x => y => x + y
console.log(curried(1)(2)) // 3


// Currying (fCC example 2)
function add(x) {
  return function(y) {
    return function(z) {
      return x + y + z
    }
  }
}
console.log(add(10)(20)(30)) // 60

//-------------------------------------------------------------------

// JS
// Closure example 1
const myAlert = () => {
  const x = "Help! I think I found a clue!"
  const alerter = () => {
    alert(x)
  }
  alerter()
}

myAlert() // Help! I think I found a clue!
// (but instead of being logged, it's a window that appears, due to how 'alert()' works).


// Closure example 2
const myAlert = () => {
  const x = "Help! I think I found a clue!"
  const alerter = () => {
    alert(x)
  }
  setTimeout(alerter, 1000)
  console.log('Does the alert or log happen first?') // Does the alert or log happen first?
}

myAlert() // Help! I think I found a clue!
// (but instead of being logged, it's a window that appears, due to how 'alert()' works).

// Note that 'setTimeout...' is called before 'console.log...', as you'd think, but is not
// visible because it goes away "deep into browser land" for 1 second, and, during this
// time, 'Does the alert or log happen first?' is logged.


// Closure example 3
const myAlert = () => {
  const x = "Help! I think I found a clue!"
  let count = 0
  const alerter = () => {
    alert(`${x} ${++count}`)
  }
  return alerter // This is returning whatever value is held in 'alerter',...
  // ...and the value that is held is the body of a function,...
}

const funcAlert = myAlert() // ...so the body of the function is now stored in 'funcAlert',...
const funcAlert2 = myAlert()
funcAlert() // ...and now 'funcAlert' is called, and logged is: Help! I think I found a clue! 1
// (but instead of being logged, it's a window that appears, due to how 'alert()' works).
// And as you'd expect, 'funcAlert()' makes the alert appear, but actually RETURNS 'undefined'.
funcAlert() // Help! I think I found a clue! 2
// ('2' this time because here we're not re-creating the whole "execution context" (i.e., from
// 'const myAlert'), but are entering straight into the 'alerter' function body, and 'count'
// was already storing '1' from the first 'funcAlert' call.)
funcAlert2() // Help! I think I found a clue! 1
// ('1' because a new "execution context" was created, i.e., starting from 'const myAlert'.)

//-------------------------------------------------------------------

// JS
// Closure example 4
const newClue = (name) => {
  const length = name.length
  return (weapon) => {
    let clue = length + weapon.length
    return !!(clue % 2) // note that '!!' here functions the same way as 'Boolean' would
  }
}

const didGreenDoItWithA = newClue('Green')
console.log(didGreenDoItWithA) // the function body that the 'newClue' function returns:
/*
(weapon) => {
    let clue = length + weapon.length
    return !!(clue % 2) // note that '!!' here functions the same way as 'Boolean' would
  }
*/
console.log(didGreenDoItWithA('candlestick')) // false
console.log(didGreenDoItWithA('lead pipe')) // false
// the point of including the above line is that its function call still 'remembers'
// that 'Green' is the argument passed in to the 'newClue' function

//-------------------------------------------------------------------

// JS
// Closure example 5
const countClues = () => {
  let n = 0
  return { // note this is returning an object with two functions in it
    count: () => ++n,
    reset: () => n = 0
  }
}

let counter = countClues()
let counter2 = countClues()

console.log(counter.count()) // 1
console.log(counter.count()) // 2
console.log(counter.count()) // 3
console.log(counter.reset()) // 0

console.log(counter2.count()) // 1
console.log(counter2.count()) // 2
console.log(counter2.count()) // 3
console.log(counter2.reset()) // 0

console.log(counter.count()) // 1

console.log(counter2.count()) // 1

console.log(counter.count()) // 2

//-------------------------------------------------------------------

// JS
// Closure example (Gandolfo's "Closure Recipe")
let scope = 'global scope'
function checkScope() {
  let innerVar = 'local scope'
  function innerFunc() {
    return innerVar
  }
  return innerFunc
}

let test = checkScope()
console.log(test) // the body of the 'innerFunc' function is logged
console.log(test()) // local scope

//-------------------------------------------------------------------

// JS
// This example shows why it's really important to understand
// the order in which functions execute
const findSomeone = () => {
  const speak = () => {
    console.log(who) // Why hello there, Prof Plum!
  }
  let who = 'Why hello there, Prof Plum!'
  return speak
}

const someoneSpeak = findSomeone()
console.log(someoneSpeak()) // undefined

//-------------------------------------------------------------------

// JS
// This closure example really shows why it's important to understand the order in which the code executes.
// Based on Robert's response to my questions:
// The *order* in which the lines of code run is annotated.
const makeTimer = () => {
  let elapsed = 0 // *2*

  const stopwatch = () => { return elapsed } // *3* // Creates a function local to the 'makeTimer' function.

  const increase = () => elapsed++ // *4* // Creates a function local to the 'makeTimer' function.

  setInterval(increase, 1000) // *5* // Calls 'setInterval', to which the 'increase' function is passed, and 'increase' is not yet run but is *queued to run* (more on this below).

  return stopwatch // *6*
}

let timer = makeTimer() // *1* (But note that this 'makeTimer' function call is not actually stored in 'timer' until 'makeTimer' has run, and that what exactly gets stored at that point is simply what 'makeTimer' returns, which is the 'stopwatch' function body.)

// *7*: With every call of 'timer' in the console (which is effectively calling the 'stopwatch' function and therefore returning 'elapsed'), logged is the number of seconds since 'makeTimer' was called and hence 'elapsed' was initialized (and 'makeTimer' being called is also what *queues up to run* 'increase', which increments 'elapsed'). Why "queues up"? Because 'elapsed' is actually returned (as a result of the calling of 'timer') BEFORE the incrementing of 'elapsed' ever reaches the one second mark, but AFTER the incrementing of 'elapsed' is queued (which, again, occurs during the calling of 'makeTimer').
// (Note that there's nothing in this code that will ever stop the incrementing and that in reality this would have to be remedied.)

// Closure as it relates to this example: Even though the call stack is cleared after the 'makeTimer' function runs, any variable still needed for reference is "remembered". In this case, 'stopwatch' retains a link to what's needed, e.g., anything affecting 'elapsed'.      

//-------------------------------------------------------------------

// JS
// CREATING a 'curry' function
const curry = (fn) => {
  return (arg) => {
    return (arg2) => {
      return fn(arg, arg2)
    }
  }
}

// USING the 'curry' function created above
const func = function(a, b) {
  return [a, b]
}
const curried = curry(func)
console.log(curried(1)(2)) // [1, 2]

//-------------------------------------------------------------------

// JS
// CREATING a 'compose' function
const compose = (fn, fn2) => {
  return (arg) => {
    const result = fn2(arg)
    return fn(result) 
  }
}

// USING the 'compose' function created above
const consider = (name) => {
  return `I think it could be...${name}`
}

const exclaim = (statement) => {
  return `${statement.toUpperCase()}!`
}

const blame = compose(consider, exclaim)

console.log(blame('you')) // I think it could be...YOU!
Enter fullscreen mode Exit fullscreen mode

Top comments (0)