This commit is contained in:
Elizabeth Hunt 2024-02-08 19:35:56 -07:00
parent 5b8b3abcba
commit 05572d8404
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
5 changed files with 141 additions and 15 deletions

View File

@ -59,15 +59,25 @@ We're in love with its predictability, the assurance that no matter what we give
But let's imagine, for a moment, a different kind of relationship. One where actions outside the relationship influences responses. But let's imagine, for a moment, a different kind of relationship. One where actions outside the relationship influences responses.
Imagine asking your partner about what food they want to eat. But instead of a straightforward answer based on your question alone and some state (i.e. hunger levels, craving ratios, etc), the function's response is influenced by other factors; the day of the week, and the state of the day or week prior. We can simulate this: (go through some ratios and outputs). Imagine asking your partner about what food they want to eat. But instead of a straightforward answer based on your question alone and some state (i.e. hunger levels, craving ratios, etc), the function's response is influenced by other factors; the day of the week, and the state of the day or week prior. We can simulate this: ((go through the ratios and outputs)).
Let's see what causes this in this black box we don't love; don't pay attention to the implementation (there's some stuff we haven't talked about yet), but check out this line (MATH.RANDOM). This is a side effect; an unpredictible effect on the output. Let's see what causes this in this black box we don't love; don't pay attention to the implementation (there's some stuff we haven't talked about yet), but check out this line (MATH.RANDOM). This is a side effect; an unpredictible effect on the output.
Side effects introduce unpredictability into our programming relationships. Where output is not just determined by its input but also by the state of the outside world, or the system at the time of execution. Suddenly, the predictability we cherished is compromised. (we'll go much more in depth about this more later) Side effects introduce unpredictability into our programming relationships. Where output is not just determined by its input but also by the state of the outside world, or the system at the time of execution. Suddenly, the predictability we cherished is compromised.
So let's take our love of this function and begin studying it. We call a function that is unpredictable, or has side effects, "impure", and one that isn't - "pure".
We love our black boxes a lot, and we want to share them with other people in our lives. So let's create another that takes a list of people and creates a custom letter for them. It was obvious how we could cause unpredictability by including randomness. On the left we have two impure functions.
When we execute the first block, we get the expected result of `fib(5)`.
But because `fact` uses the same cache, we'll run fact(5)... `fact(5)` returns fib(5)!
Now, when I go and update the cache to be correct, we get the correct answer. I was able to change the behavior of the function by modifying stuff outside of it. Get the gist - impure vs pure functions, side effects, etc? Cool.
For now let's move back to studying our pure black boxes.
We love our black boxes a lot, and we want to share them with other people in our lives. So let's create another that takes a list of people and creates a custom valentine's letter for them.
```python ```python
def make_valentines_letters(people): def make_valentines_letters(people):

View File

@ -133,8 +133,13 @@ export class FunctionBox extends Node {
</Node> </Node>
</Rect> </Rect>
<Rect direction={"column"} height={"100%"} alignItems={"end"}> <Rect
<Rect direction={"row"} alignItems={"center"} gap={10}> direction="column"
height={"100%"}
alignItems={"end"}
justifyContent="center"
>
<Rect direction="row" alignItems="center" gap={10}>
<Line <Line
points={[]} points={[]}
stroke={theme.red.hex} stroke={theme.red.hex}
@ -159,7 +164,7 @@ export class FunctionBox extends Node {
public *resetInput(duration: number) { public *resetInput(duration: number) {
yield* all( yield* all(
...this.inputs.map((x) => ...this.inputs.map((x) =>
all(x.opacity(0, duration), x.fontSize(0, duration)), all(x.opacity(1, duration), x.fontSize(this.inputFontSize, duration)),
), ),
...this.inputSegments.map((segment) => ...this.inputSegments.map((segment) =>
all(segment.points([], duration), segment.opacity(1, duration)), all(segment.points([], duration), segment.opacity(1, duration)),
@ -232,14 +237,13 @@ export class FunctionBox extends Node {
public *propogateOutput(duration: number) { public *propogateOutput(duration: number) {
yield* all( yield* all(
this.boxMoji().text(this.idlingText, duration),
this.child()?.opacity(0.2, duration), this.child()?.opacity(0.2, duration),
this.output().opacity(0.2, duration), this.output().opacity(0.2, duration),
this.output().fontSize(this.outputFontSize, duration),
this.outputSegment().opacity(0, duration), this.outputSegment().opacity(0, duration),
); );
const output = this.function(...this.currentArgs.map((input) => input.val)); const output = this.function(...this.currentArgs.map((input) => input.val));
switch (typeof output) { switch (typeof output) {
case "function": case "function":
yield this.output().add( yield this.output().add(
@ -271,6 +275,7 @@ export class FunctionBox extends Node {
} }
yield* all( yield* all(
this.boxMoji().text(this.idlingText, duration),
this.outputSegment().points( this.outputSegment().points(
[ [
{ x: 0, y: 0 }, { x: 0, y: 0 },

View File

@ -5,13 +5,15 @@ import flirtingwithfunctions from "./flirtingwithfunctions?scene";
import doctor from "./doctor?scene"; import doctor from "./doctor?scene";
import first_box from "./first_box?scene"; import first_box from "./first_box?scene";
import hungry_partner from "./hungry_partner?scene"; import hungry_partner from "./hungry_partner?scene";
import pure_functions from "./pure_functions?scene";
export const scenes = [ export const scenes = [
// title, title,
// me, me,
// partone, partone,
// flirtingwithfunctions, flirtingwithfunctions,
// doctor, doctor,
// first_box, first_box,
hungry_partner, hungry_partner,
pure_functions,
]; ];

View File

@ -0,0 +1,5 @@
{
"version": 0,
"timeEvents": [],
"seed": 1453873557
}

View File

@ -0,0 +1,104 @@
import { Node, Rect, Txt, makeScene2D } from "@motion-canvas/2d";
import {
Direction,
all,
beginSlide,
createRef,
slideTransition,
} from "@motion-canvas/core";
import { FunctionBox } from "../components/function_box";
import { theme } from "../theme";
const pureFunction = `const pureFib = (n: number) => {
if (n <= 1) {
retun 1;
}
return fib(n - 1) + fib(n - 2);
};
pureFib;`;
const impureFibFunction = `window.cache = [1, 1];
const impureFib = (n: number) => {
while (cache.length <= n + 1)
cache.push(cache.at(-1) + cache.at(-2));
return cache[n];
};
impureFib;`;
const impureFactFunction = `const impureFact = (n: number): number => {
while (cache.length <= n)
cache.push(cache.length * cache[cache.length - 1]);
return cache[n];
};
impureFact;`;
export default makeScene2D(function* (view) {
const impureFibFunctionBox = createRef<FunctionBox>();
const impureFactFunctionBox = createRef<FunctionBox>();
const pureFunctionBox = createRef<FunctionBox>();
view.add(
<Rect direction="row" gap={300} layout>
<Rect direction="column" alignItems="center" gap={20}>
<Txt fontSize={30} fontFamily={theme.font} fill={theme.text.hex}>
IMPURE
</Txt>
<FunctionBox
arity={1}
idlingText=""
workingText=""
source={impureFibFunction}
ref={impureFibFunctionBox}
/>
<FunctionBox
arity={1}
idlingText=""
workingText=""
source={impureFactFunction}
ref={impureFactFunctionBox}
/>
</Rect>
<Rect direction="column" alignItems="center" gap={20}>
<Txt fontSize={30} fontFamily={theme.font} fill={theme.text.hex}>
PURE
</Txt>
<FunctionBox
arity={1}
idlingText=""
workingText=""
source={pureFunction}
ref={pureFunctionBox}
/>
</Rect>
</Rect>,
);
yield* slideTransition(Direction.Right);
yield* beginSlide("Pure vs Impure");
yield* all(
impureFibFunctionBox().showCode(0.5),
impureFactFunctionBox().showCode(0.5),
);
yield* impureFibFunctionBox().setInputs([{ val: 5 }], 0.05);
yield* beginSlide("Show FibImpure(5)");
yield* impureFibFunctionBox().propogateInput(0.05);
yield* impureFibFunctionBox().propogateOutput(0.05);
yield* beginSlide("FibImpure(5)");
yield* impureFactFunctionBox().setInputs([{ val: 5 }], 0.05);
yield* beginSlide("Show FactImpure(5)");
yield* impureFactFunctionBox().propogateInput(0.05);
yield* impureFactFunctionBox().propogateOutput(0.05);
yield* beginSlide("FactImpure(5)");
yield* impureFactFunctionBox().reset(0.15);
yield* impureFactFunctionBox().setInputs([{ val: 5 }], 0.15);
yield* beginSlide("FactImpure(5) Add 5");
yield* impureFactFunctionBox().propogateInput(0.05);
yield* impureFactFunctionBox().propogateOutput(0.05);
yield* beginSlide("FactImpure(5) Correct");
});