type Html = | Element(String, List>, List>) | Text(String) | Empty type Attr = | Class(String) | Id(String) | OnClick(M) fn div(attrs: List>, children: List>): Html = Element("div", attrs, children) fn span(attrs: List>, children: List>): Html = Element("span", attrs, children) fn h1(attrs: List>, children: List>): Html = Element("h1", attrs, children) fn button(attrs: List>, children: List>): Html = Element("button", attrs, children) fn text(content: String): Html = Text(content) fn class(name: String): Attr = Class(name) fn onClick(msg: M): Attr = OnClick(msg) type Model = | Counter(Int) fn getCount(model: Model): Int = match model { Counter(n) => n, } fn init(): Model = Counter(0) type Msg = | Increment | Decrement | Reset fn update(model: Model, msg: Msg): Model = match msg { Increment => Counter(getCount(model) + 1), Decrement => Counter(getCount(model) - 1), Reset => Counter(0), } fn viewCounter(count: Int): Html = { let countText = text(toString(count)) let countSpan = span([class("count")], [countText]) let displayDiv = div([class("counter-display")], [countSpan]) let minusBtn = button([onClick(Decrement), class("btn")], [text("-")]) let resetBtn = button([onClick(Reset), class("btn btn-reset")], [text("Reset")]) let plusBtn = button([onClick(Increment), class("btn")], [text("+")]) let buttonsDiv = div([class("counter-buttons")], [minusBtn, resetBtn, plusBtn]) let title = h1([], [text("Counter")]) div([class("counter-app")], [title, displayDiv, buttonsDiv]) } fn view(model: Model): Html = viewCounter(getCount(model)) fn showAttr(attr: Attr): String = match attr { Class(s) => "class=\"" + s + "\"", Id(s) => "id=\"" + s + "\"", OnClick(msg) => match msg { Increment => "onclick=\"Increment\"", Decrement => "onclick=\"Decrement\"", Reset => "onclick=\"Reset\"", }, } fn showAttrs(attrs: List>): String = match List.head(attrs) { None => "", Some(a) => match List.tail(attrs) { None => showAttr(a), Some(rest) => showAttr(a) + " " + showAttrs(rest), }, } fn showChildren(children: List>, indent: Int): String = match List.head(children) { None => "", Some(c) => match List.tail(children) { None => showHtml(c, indent), Some(rest) => showHtml(c, indent) + showChildren(rest, indent), }, } fn showHtml(html: Html, indent: Int): String = match html { Empty => "", Text(s) => s, Element(tag, attrs, children) => { let attrStr = showAttrs(attrs) let attrPart = if String.length(attrStr) > 0 then " " + attrStr else "" let childStr = showChildren(children, indent + 2) "<" + tag + attrPart + ">" + childStr + "" }, } fn main(): Unit with {Console} = { let model = init() Console.print("=== Counter App (TEA Pattern) ===") Console.print("") Console.print("Initial count: " + toString(getCount(model))) Console.print("") let m1 = update(model, Increment) Console.print("After Increment: " + toString(getCount(m1))) let m2 = update(m1, Increment) Console.print("After Increment: " + toString(getCount(m2))) let m3 = update(m2, Increment) Console.print("After Increment: " + toString(getCount(m3))) let m4 = update(m3, Decrement) Console.print("After Decrement: " + toString(getCount(m4))) let m5 = update(m4, Reset) Console.print("After Reset: " + toString(getCount(m5))) Console.print("") Console.print("=== View (HTML Structure) ===") Console.print(showHtml(view(m2), 0)) } let output = run main() with {}