[#230] Add e-commerce store example app#255
Merged
milkyskies merged 42 commits intomainfrom Mar 17, 2026
Merged
Conversation
Second example app showcasing advanced Floe features not covered by the todo app: branded types (ProductId, OrderId), nested error unions with multi-depth matching, range matching, tuples, traits, partial application, and default parameter values. Uses DummyJSON as mock API with TanStack Router + Query, React, Tailwind, and Zod. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use the Gleam/Rust pattern: `type ProductId = ProductId(number)` instead of `Brand<number, "ProductId">`. Single-variant unions are a natural newtype wrapper that reuses existing syntax (constructors, match, ==). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrites all .fl files to use the full vision of the language:
- Implicit returns (no return keyword)
- parse<T> built-in instead of Zod schemas
- Http.get/Http.json namespaced stdlib
- Pipe into match (value |> match { ... })
- Array pattern matching ([] and [first, ..rest])
- collect block for checkout validation
- Record type composition (...WithRating)
- deriving (Eq) on Product
- Number literal separators (10_000)
- Array.sum, Array.isEmpty stdlib additions
- Blank line before final expression (formatter convention)
This code won't pass floe check yet — it exercises features
from issues #257-#275 that haven't been implemented.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- product.fl: Replace Array.from with String.repeat, fix Array.sum by using Array.reduce with typed params, fix partial application placeholders with lambdas, use ProductId instead of number - api.fl: Change fetchCategories return type to Array<CategoryResponse> to match checker inference, export CategoryResponse type - checkout.fl: Replace validateCheckout(_, shipping) placeholder with direct call validateCheckout(cart, shipping) - cart.fl: Use ProductId in callback types, remove unused imports, prefix unused variable with underscore - catalog.fl: Replace tuple arrays with helper functions for price/sort buttons, fix operator precedence in comparisons, use parse<T> for trusted import data, fix sortProducts partial application - product-detail.fl: Replace Array.from with String.repeat for stars, use parse<T> for trusted import data, wrap Array.map in div for consistent match arm types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace PriceRange.Any and OrderStatus.Pending with unqualified variants Any and Pending. Dot-based variant access doesn't work reliably (will be replaced by :: syntax in #324). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Result uses { ok: true, value } / { ok: false, error }, not
{ tag: "Ok" } / { tag: "Err" }. Match codegen now checks
.ok === true/false and accesses .value/.error correctly.
Also fix Filter.All → All in todo app home.fl.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
const x = expr? now emits:
const _r0 = expr;
if (!_r0.ok) return _r0;
const x = _r0.value;
Instead of the broken: const x = expr!
Also fix Result match codegen to use .ok/.error fields,
and fix destructured param codegen for { } and [ ] patterns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of emitting deeply nested IIFEs with throw for chained pipe+unwrap expressions like `await Http.get(url)? |> Http.json? |> parse<T>?`, flatten the chain into sequential _rN temp variables with early return checks: const _r0 = await Http.get(...); if (!_r0.ok) return _r0; const _r1 = await Http.json(_r0.value); if (!_r1.ok) return _r1; const _r2 = parse(_r1.value); if (!_r2.ok) return _r2; const data = _r2.value; Also auto-detects async stdlib IIFEs (e.g. Http.json) and adds await. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
props.selectedCategory == cat.slug |> match parsed as props.selectedCategory == (cat.slug |> match) due to pipe binding tighter than ==. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tuple (a, b) now emits [a, b] instead of [a, b] as const. The as const caused [x, y] as const[0] to be parsed wrong by esbuild, making tuple element access always return the array instead of the element. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
data from useSuspenseQuery is already a Result — don't parse<T> it again. Just match Ok/Err to unwrap the tuple value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Vite plugin now passes file path to floe build instead of piping stdin, enabling cross-file import resolution - Added cmd_build_file_stdout for --emit-stdout with a file path - floe build now emits code even with checker warnings (prints to stderr instead of blocking). floe check still reports errors. - Fixed store code: unwrap Results directly instead of re-parsing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stdlib definition had (arr, initial, callback) but docs/usage showed (arr, callback, initial). Fixed to match docs and JS convention. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OrderId is a newtype wrapping number. In string interpolation, access .value to display the number instead of [object Object]. TODO: compiler should auto-unwrap newtypes in string interpolation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Capture stderr from floe build instead of letting it print directly. Checker warnings about unknown types from trusted imports are noise — the code compiles and runs correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a Floe function (e.g. fetchProducts) is passed to a generic npm
function (e.g. useSuspenseQuery({ queryFn: async || fetchProducts() })),
tsgo couldn't infer the generic type because the function was undefined
in the probe file.
Two fixes:
1. In tsgo.rs, emit `declare function` stubs for all functions from
resolved .fl imports, with proper parameter types and return types
(including optional params and async/Promise wrapping).
2. In dts.rs, handle TSTypeOperatorType (readonly) by unwrapping to
the inner type, fixing `readonly [T, U]` tuples being parsed as
`unknown`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Confirmed(OrderId(n)) properly destructures the newtype via multi-depth pattern matching, fixing checker error E006. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
emit_pattern_condition was always using `.value` for single-field variant nested conditions, while collect_bindings correctly used variant_info to resolve the actual field name (e.g. `orderId`). This caused runtime errors like "Cannot read properties of undefined (reading 'tag')" when matching Confirmed(OrderId(n)) because the codegen emitted `.value.tag` instead of `.orderId.tag`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Root layout uses h-screen flex column so nav is pinned - Main content area scrolls independently with overflow-y-auto - Sidebar is sticky with self-start so it stays visible while products scroll Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #230
Summary
examples/store/that exercises advanced Floe features not covered by the todo appLanguage features showcased
ProductId,OrderId)types.flApiError > NetworkError)types.fl,errors.flNetwork(Timeout(ms)))errors.fl400..499,500..599)errors.flDisplay,Discountable)types.fl,product.fl(subtotal, discount, total))product.fl,api.flcompareBy(order, _, _))product.flapi.fl(fetchProducts)product.fl,errors.fl?operator for Result chainingapi.fltry awaitfor async error handlingapi.flcatalog.fl,product-detail.flproduct.fl,catalog.fltap/unreachable/ match guardsApp structure
Test plan
floe check examples/store/src/passespnpm devinexamples/store/loads the catalog🤖 Generated with Claude Code