3 ms·
The code in the red is imperative, with implicit state all over the place. I would argue that it's not linear, you have to keep all those mutable values and sta
by weatherlight 3y ago
The code in the red is imperative, with implicit state all over the place. I would argue that it's not linear, you have to keep all those mutable values and state transitions in your head as you read what's happening from top to bottom.
It's not extensible, It's not composable, It's hard to test, and frankly, it's complicated and complex for no other reason than a particular type of engineer thinks its easier to read, because they feel all programming should be imperative.
Don't get me wrong, there's a time and a place for this style of programming. (Manual memory management, algorithmic design that maximizes speed or memory usage, or even taking a bunch of services and dictating the order in which they are supposed to be executed.)
For business logic, especially the type thats supposed to model "the world," this is terrible code.
How is the bottom, not better code?
type Oven struct {
Temp int
}
type Box struct {
// Box properties
}
type Pizza struct {
Base string
Sauce string
Cheese string
Toppings []string
Baked bool
Boxed bool
Sliced bool
Ready bool
}
type Order struct {
Size string
Sauce string
Kind string
}
func preparePizza(order *Order) *Pizza {
toppings := map[string][]string{
"Veg": []string{"Tomato", "Bell Pepper"},
"Meat": []string{"Pepperoni", "Sausage"},
}
return &Pizza{
Base: order.Size,
Sauce: order.Sauce,
Cheese: "Mozzarella",
Toppings: toppings[order.Kind],
}
}
func bakePizza(oven *Oven, pizza *Pizza, cookingTemp int, checkOvenInterval int) *Pizza {
// Simulate oven heating
for oven.Temp < cookingTemp {
time.Sleep(time.Duration(checkOvenInterval) * time.Millisecond)
oven.Temp += 10 // Simulate oven heating
}
pizza.Baked = true
return pizza
}
func boxPizza(pizza *Pizza, order *Order) *Pizza {
box := &Box{}
pizza.Boxed = true // Simulate putting pizza in box
pizza.Sliced = true // Simulate slicing pizza
pizza.Ready = true // Simulate closing box
return pizza
}
// I just need to really understand this imperative part
// this is the meat and potatoes
func createPizza(order *Order, oven *Oven, cookingTemp int, checkOvenInterval int) *Pizza {
pizza := preparePizza(order)
pizza = bakePizza(oven, pizza, cookingTemp, checkOvenInterval)
pizza = boxPizza(pizza, order)
return pizza
}
- weatherlight 3y agoNow in a Functional lang, that has good scoping and private functions, etc. defmodule Pizza do import Pizza.Pizza, only: [new: 0] import Pizza.Order, only: [new: 0] def create_pizza(order, oven_temp) do order |> prepare_pizza() |> bake_pizza(oven_temp) |> box_pizza() end defp prepare_pizza(%Pizza.Order{size: size, sauce: sauce, kind: kind}) do toppings = case kind do "Veg" -> ["Tomato", "Bell Pepper"] "Meat" -> ["Pepperoni", "Sausage"] _ -> [] end %Pizza.Pizza{base: size, sauce: sauce, toppings: toppings} end defp bake_pizza(oven_temp, %Pizza.Pizza{baked: _baked} = pizza) when oven_temp >= 400 do %Pizza.Pizza{pizza | baked: true} end defp bake_pizza(_, pizza), do: pizza defp box_pizza(%Pizza.Pizza{boxed: _boxed, sliced: _sliced, ready: _ready} = pizza) do %Pizza.Pizza{pizza | boxed: true, sliced: true, ready: true} end end