add generatlized
This commit is contained in:
parent
c55d9d2832
commit
512c245466
35
script.md
35
script.md
@ -105,36 +105,11 @@ There, now, we can make sure our friends are happy on their birthdays!
|
||||
|
||||
But, this is getting annoying; what about Christmas, Thanksgiving, or Easter? Making a new black box to make a list of new cards, going through each person, every single time to construct a list of cards, is getting really tedious.
|
||||
|
||||
What if we generalized this? We create a bunch of black boxes that take a person, and generate them a card, specifically; like a template for a card you could print off and fill in manually.
|
||||
[SPACE] What if we generalized this? [SPACE] We create a couple of black boxes that take a person, and generate them a card, specifically; like a template for a card you could print off and fill in manually. Like this, for alan turing.
|
||||
|
||||
```python
|
||||
def valentine_letter(person):
|
||||
return f"Dear, {person.name}\nYour smile lights up my world. Happy Valentine's Day!")
|
||||
Then, we can use a new black box that takes a list of people, and applies this template to each person.
|
||||
|
||||
def birthday_card(person):
|
||||
today = new Date()
|
||||
daysUntilBirthday = toDays(new Date(person.birthday, today.year) - today)
|
||||
newAge = today.year - person.birthday.year
|
||||
|
||||
card = f"Happy Birthday {name}\nI can't believe you're already {newAge} years old!"
|
||||
cards.append({ "message": card, "deliverInDays": daysUntilBirthday })
|
||||
```
|
||||
|
||||
Then, we can use a black box that takes a list of people, and applies this template to each person.
|
||||
|
||||
```
|
||||
def buildCards(people, cardMaker):
|
||||
cards = []
|
||||
for person in people:
|
||||
card = cardMaker(person)
|
||||
cards.append(card)
|
||||
return cards
|
||||
|
||||
people = [{"name": "Joseph", birthday: new Date()}, {"name": "DeeDee", birthday: new Date()}]
|
||||
buildCards(people, birthdayCard)
|
||||
```
|
||||
|
||||
The ability in a language to pass a function around like this - like a variable - is what makes functions "first class". And the `buildCards` function takes a function as input, making it a "higher order function". (TODO: slides)
|
||||
The ability in a language to pass a function around like this - like a variable - is what makes functions "first class". And the `buildCards` function takes a function as input, making it a "higher order function".
|
||||
|
||||
Functional Reproduction
|
||||
===
|
||||
@ -211,7 +186,7 @@ Immutability
|
||||
===
|
||||
We briefly mentioned side effects and alluded them to the unpredictability of a partner. We love our black boxes because they're reliable.
|
||||
|
||||
But, we've actually had an impostor among us (AMOGUS sound??) throughout this presentation. Specifically in ~buildCards~:
|
||||
But, we've actually had an impostor among us (AMOGUS sound??) throughout this presentation. Specifically in `buildCards`:
|
||||
|
||||
```
|
||||
def buildCards(people, cardMaker):
|
||||
@ -244,7 +219,7 @@ def build_cards(people, card_maker):
|
||||
return [card] + build_cards(rest_people, card_maker)
|
||||
```
|
||||
|
||||
Here we're not changing anything at all (except the stack), with the same functionality; this code is "immutable". When we call ~build_cards~ on a list of people, we're 100% certain we're not gonna get something odd short of a bug.
|
||||
Here we're not changing anything at all (except the stack), with the same functionality; this code is "immutable". When we call `build_cards` on a list of people, we're 100% certain we're not gonna get something odd short of a bug.
|
||||
|
||||
At a high level there are so many benefits to immutability:
|
||||
+ Concurrency (TODO: go into more detail)
|
||||
|
@ -86,7 +86,7 @@ export class FunctionBox extends Node {
|
||||
alignItems={"center"}
|
||||
layout
|
||||
>
|
||||
<Rect direction={"column"} alignItems={"end"}>
|
||||
<Rect direction={"column"} alignItems={"end"} gap={10}>
|
||||
{range(this.arity).map((i) => (
|
||||
<Rect direction={"row"} alignItems={"center"} gap={10}>
|
||||
<Rect
|
||||
@ -116,8 +116,9 @@ export class FunctionBox extends Node {
|
||||
fill={theme.crust.hex}
|
||||
lineWidth={4}
|
||||
padding={60}
|
||||
direction={"row"}
|
||||
height={"100%"}
|
||||
direction="row"
|
||||
height="100%"
|
||||
alignItems="center"
|
||||
>
|
||||
<Txt fontFamily={theme.font} fill={theme.text.hex} ref={this.boxMoji}>
|
||||
{this.idlingText}
|
||||
@ -273,7 +274,6 @@ export class FunctionBox extends Node {
|
||||
<CodeBlock
|
||||
fontFamily={theme.font}
|
||||
language="typescript"
|
||||
fontSize={this.outputFontSize}
|
||||
code={JSON.stringify(output, null, 2)}
|
||||
></CodeBlock>,
|
||||
);
|
||||
|
@ -13,13 +13,13 @@ import { FunctionBox } from "../components/function_box";
|
||||
import { theme } from "../theme";
|
||||
import { PEOPLE, Person, PersonI } from "../components/person";
|
||||
|
||||
const daysUntilNextBirthday = (birthDate: Date): number => {
|
||||
export const daysUntilNextDate = (date: Date): number => {
|
||||
const today = new Date();
|
||||
|
||||
const nextBirthday = new Date(
|
||||
today.getFullYear(),
|
||||
birthDate.getMonth(),
|
||||
birthDate.getDate(),
|
||||
date.getMonth(),
|
||||
date.getDate(),
|
||||
);
|
||||
if (today > nextBirthday) nextBirthday.setFullYear(today.getFullYear() + 1);
|
||||
|
||||
@ -45,7 +45,7 @@ export const birthdayCardsFor = (people: PersonI[]): CardI[] => {
|
||||
`Happy ${age}${ending} birthday, ${person.name}!\n` +
|
||||
"I can't believe it's already been a year!";
|
||||
|
||||
const deliverInDays = daysUntilNextBirthday(person.birthday);
|
||||
const deliverInDays = daysUntilNextDate(person.birthday);
|
||||
cards.push({ deliverInDays, message });
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ const birthdayCardsFor = (people: PersonI[]): CardI[] => {
|
||||
\`Happy \${age}\${ageEnding} birthday, \${person.name}!\\n\` +
|
||||
"I can't believe it's already been a year!";
|
||||
|
||||
const deliverInDays = daysUntilNextBirthday(person.birthday);
|
||||
const deliverInDays = daysUntilNextDate(person.birthday);
|
||||
cards.push({ deliverInDays, message });
|
||||
}
|
||||
|
||||
|
5
src/scenes/generalized.meta
Normal file
5
src/scenes/generalized.meta
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": 0,
|
||||
"timeEvents": [],
|
||||
"seed": 3138224359
|
||||
}
|
212
src/scenes/generalized.tsx
Normal file
212
src/scenes/generalized.tsx
Normal file
@ -0,0 +1,212 @@
|
||||
import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
|
||||
import {
|
||||
Direction,
|
||||
all,
|
||||
beginSlide,
|
||||
createRef,
|
||||
slideTransition,
|
||||
} from "@motion-canvas/core";
|
||||
|
||||
import { FunctionBox } from "../components/function_box";
|
||||
import { PEOPLE, Person, PersonI } from "../components/person";
|
||||
import { CardI, daysUntilNextDate } from "./birthday_letters";
|
||||
|
||||
const valentineCardFn = (person: PersonI): CardI => {
|
||||
const valentinesDay = new Date("02/14/2024");
|
||||
const message =
|
||||
`Dear, ${person.name}\n.` +
|
||||
"Your smile lights up my world.\n" +
|
||||
"Happy Valentine's Day!";
|
||||
const deliverInDays = daysUntilNextDate(valentinesDay);
|
||||
return { message, deliverInDays };
|
||||
};
|
||||
|
||||
const birthdayCardFn = (person: PersonI): CardI => {
|
||||
const age = new Date().getFullYear() - person.birthday.getFullYear();
|
||||
const ending =
|
||||
({ 1: "st", 2: "nd", 3: "rd" } as Record<number, string>)[
|
||||
parseInt(Array.from(age.toString()).at(-1))
|
||||
] ?? "th";
|
||||
const message =
|
||||
`Happy ${age}${ending} birthday, ${person.name}!\n` +
|
||||
"I can't believe it's already been a year!";
|
||||
|
||||
const deliverInDays = daysUntilNextDate(person.birthday);
|
||||
return { deliverInDays, message };
|
||||
};
|
||||
|
||||
const valentineCardGenerator = `const valentineCardGenerator = (person: PersonI): CardI => {
|
||||
const valentinesDay = new Date("02/14/2024");
|
||||
const message =
|
||||
\`Dear, \${person.name}\\n.\` +
|
||||
"Your smile lights up my world.\\n" +
|
||||
"Happy Valentine's Day!";
|
||||
const deliverInDays = daysUntilNextDate(valentinesDay);
|
||||
return { message, deliverInDays };
|
||||
};`;
|
||||
|
||||
const birthdayCardGenerator = `const birthdayCardGenerator = (person: PersonI): CardI => {
|
||||
const age = new Date().getFullYear() - person.birthday.getFullYear();
|
||||
const ageEnding = (
|
||||
{ 1: "st", 2: "nd", 3: "rd" } as Record<number, string>
|
||||
)[parseInt(Array.from(age.toString()).at(-1))] ?? "th";
|
||||
|
||||
const message =
|
||||
\`Happy \${age}\${ageEnding} birthday, \${person.name}!\\n\` +
|
||||
"I can't believe it's already been a year!";
|
||||
|
||||
const deliverInDays = daysUntilNextDate(person.birthday);
|
||||
return { deliverInDays, message };
|
||||
}`;
|
||||
|
||||
const cardBuilder = `const buildCards = (
|
||||
people: PersonI[],
|
||||
cardGenerator: (person: PersonI) => CardI,
|
||||
): CardI[] => {
|
||||
const cards: CardI[] = [];
|
||||
|
||||
for (const person of people) {
|
||||
const card = cardGenerator(person);
|
||||
cards.push(card);
|
||||
}
|
||||
|
||||
return cards;
|
||||
};
|
||||
buildCards;`;
|
||||
|
||||
export default makeScene2D(function* (view) {
|
||||
const valentineFunctionBox = createRef<FunctionBox>();
|
||||
const birthdayFunctionBox = createRef<FunctionBox>();
|
||||
const cardBuilderBox = createRef<FunctionBox>();
|
||||
const layout = createRef<Layout>();
|
||||
|
||||
view.add(
|
||||
<Layout direction="row" gap={200} ref={layout} layout>
|
||||
<Layout direction="column" alignItems="center" gap={20}>
|
||||
<FunctionBox
|
||||
ref={valentineFunctionBox}
|
||||
source={valentineCardGenerator}
|
||||
fn={valentineCardFn}
|
||||
idlingText="❤️"
|
||||
workingText="📝⚙"
|
||||
outputFontSize={15}
|
||||
></FunctionBox>
|
||||
<FunctionBox
|
||||
ref={birthdayFunctionBox}
|
||||
source={birthdayCardGenerator}
|
||||
fn={birthdayCardFn}
|
||||
idlingText="🎂"
|
||||
workingText="📝⚙"
|
||||
outputFontSize={15}
|
||||
></FunctionBox>
|
||||
</Layout>
|
||||
<Layout direction="column" gap={20}>
|
||||
<FunctionBox
|
||||
ref={cardBuilderBox}
|
||||
source={cardBuilder}
|
||||
idlingText="🏭"
|
||||
workingText="📝⚙"
|
||||
outputFontSize={15}
|
||||
arity={2}
|
||||
></FunctionBox>
|
||||
</Layout>
|
||||
</Layout>,
|
||||
);
|
||||
|
||||
yield* all(
|
||||
slideTransition(Direction.Left),
|
||||
...[valentineFunctionBox, birthdayFunctionBox, cardBuilderBox].map((x) =>
|
||||
x().reset(0.1),
|
||||
),
|
||||
);
|
||||
|
||||
yield* beginSlide("show all functions");
|
||||
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) =>
|
||||
x().showCode(0.5),
|
||||
),
|
||||
);
|
||||
|
||||
yield* beginSlide("show generator functions");
|
||||
|
||||
const [p1, p2, p3, p4] = PEOPLE;
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) =>
|
||||
x().hideCode(0.5),
|
||||
),
|
||||
);
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) =>
|
||||
x().setInputs([{ val: p2, node: <Person person={p2} /> }], 0.5),
|
||||
),
|
||||
);
|
||||
|
||||
yield* beginSlide("show inputs");
|
||||
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) =>
|
||||
x().propogateInput(0.5),
|
||||
),
|
||||
);
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) =>
|
||||
x().propogateOutput(0.5),
|
||||
),
|
||||
);
|
||||
yield* beginSlide("make cards");
|
||||
|
||||
yield* all(
|
||||
...[valentineFunctionBox, birthdayFunctionBox].map((x) => x().reset(0.5)),
|
||||
cardBuilderBox().showCode(0.5),
|
||||
);
|
||||
|
||||
yield* beginSlide("hide cards, show card generator");
|
||||
|
||||
yield* cardBuilderBox().setInputs(
|
||||
[
|
||||
{
|
||||
val: [p1, p2],
|
||||
node: (
|
||||
<Layout direction="row" gap={20}>
|
||||
<Person person={p1} />
|
||||
<Person person={p2} />
|
||||
</Layout>
|
||||
),
|
||||
},
|
||||
{ val: valentineCardFn, node: valentineFunctionBox() },
|
||||
],
|
||||
0.5,
|
||||
);
|
||||
yield* beginSlide("input function to function");
|
||||
|
||||
yield* cardBuilderBox().hideCode(0.5);
|
||||
yield* cardBuilderBox().propogateInput(0.5);
|
||||
yield* cardBuilderBox().propogateOutput(0.5);
|
||||
|
||||
yield* beginSlide("build valentines cards");
|
||||
|
||||
yield* all(layout().gap(0, 0.5), cardBuilderBox().reset(0.5));
|
||||
yield* cardBuilderBox().setInputs(
|
||||
[
|
||||
{
|
||||
val: [p2, p3, p4],
|
||||
node: (
|
||||
<Layout direction="row" gap={20}>
|
||||
<Person person={p2} />
|
||||
<Person person={p3} />
|
||||
<Person person={p4} />
|
||||
</Layout>
|
||||
),
|
||||
},
|
||||
{ val: birthdayCardFn, node: birthdayFunctionBox() },
|
||||
],
|
||||
0.5,
|
||||
);
|
||||
yield* beginSlide("show birthday cards input");
|
||||
|
||||
yield* cardBuilderBox().propogateInput(0.5);
|
||||
yield* cardBuilderBox().propogateOutput(0.5);
|
||||
|
||||
yield* beginSlide("build cards");
|
||||
});
|
@ -9,6 +9,7 @@ import pure_functions from "./pure_functions?scene";
|
||||
import valentines_letters from "./valentines_letters?scene";
|
||||
import birthday_letters from "./birthday_letters?scene";
|
||||
import sad_people from "./sad_people?scene";
|
||||
import generalized from "./generalized?scene";
|
||||
|
||||
export const scenes = [
|
||||
//title,
|
||||
@ -20,6 +21,7 @@ export const scenes = [
|
||||
//hungry_partner,
|
||||
//pure_functions,
|
||||
//valentines_letters,
|
||||
sad_people,
|
||||
birthday_letters,
|
||||
//sad_people,
|
||||
//birthday_letters,
|
||||
generalized,
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user