add generatlized

This commit is contained in:
Elizabeth Hunt 2024-02-12 10:33:17 -07:00
parent c55d9d2832
commit 512c245466
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
6 changed files with 235 additions and 41 deletions

View File

@ -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)

View File

@ -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>,
);

View File

@ -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 });
}

View File

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

212
src/scenes/generalized.tsx Normal file
View 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");
});

View File

@ -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,
];