Skip to content

Latest commit

 

History

History
249 lines (179 loc) · 6.73 KB

guided-examples.md

File metadata and controls

249 lines (179 loc) · 6.73 KB
id guided-examples
title Guided Examples
slug /examples/guided-examples

This chapter is example-first: each section includes complete code you can copy into a .fss file and run directly.

Choose an example

Start here when you want to… Example
Read inputs and produce a result Hello with script arguments
Learn branching without ceremony FizzBuzz with for + match
Transform real data Collections and pattern matching
Model recursive domain data Recursive records and traversal
Split a script into modules Imports + exports
Call a script from an application An embedded pricing rule
Explore typed FFI with graphics and opaque resources A rotating teapot

1) Hello with script arguments

This first example introduces:

  • reading CLI arguments through Env.Arguments,
  • pattern matching on lists,
  • string interpolation.
let name =
  match Env.Arguments with
  | head :: _ -> head
  | _ -> "Unknown"

Console.writeLine $"Hello {name} !"

Run it:

fscript hello.fss -- Ada

API references used here:

2) FizzBuzz with for + match

This example introduces:

  • helper functions,
  • modulo checks,
  • tuple matching in match.
let fizz n = n % 3 = 0
let buzz n = n % 5 = 0

for n in [0..20] do
  match (fizz n, buzz n) with
  | (true, true) -> Console.writeLine "Fizz Buzz"
  | (true, _) -> Console.writeLine "Fizz"
  | (_, true) -> Console.writeLine "Buzz"
  | _ -> Console.writeLine $"{n}"

3) Collections and pattern matching

This example introduces:

  • List.filter, List.map, List.fold,
  • List.tryHead and option matching,
  • head/tail list patterns,
  • map-pattern removal with { [key] = _; ..rest }.
let values = [1;2;3;4;5;6]

let evens = values |> List.filter (fun n -> n % 2 = 0)
let doubled = evens |> List.map (fun n -> n * 2)
let total = doubled |> List.fold (fun acc -> fun n -> acc + n) 0

Console.writeLine $"evens={evens}"
Console.writeLine $"doubled={doubled}"
Console.writeLine $"total={total}"

let firstEven = evens |> List.tryHead
match firstEven with
| Some x -> Console.writeLine $"first even: {x}"
| None -> Console.writeLine "no even value"

match doubled with
| head :: tail -> Console.writeLine $"head={head} tail={tail}"
| [] -> Console.writeLine "empty"

let scores = { ["a"] = 1; ["b"] = 2 }
let remove k m =
  match m with
  | { [key] = _; ..rest } when key = k -> rest
  | _ -> m

let removedB = remove "b" scores
Console.writeLine $"scores without b = {removedB}"

4) Recursive records and traversal

This example introduces:

  • recursive record types (type rec),
  • optional child nodes,
  • recursive tree traversal.
type rec Node =
  { Value: int
    Left: Node option
    Right: Node option }

let leaf1 = { Value = 4; Left = None; Right = None }
let leaf2 = { Value = 5; Left = None; Right = None }
let leaf3 = { Value = 6; Left = None; Right = None }
let leaf4 = { Value = 7; Left = None; Right = None }

let n2 = { Value = 2; Left = Some leaf1; Right = Some leaf2 }
let n3 = { Value = 3; Left = Some leaf3; Right = Some leaf4 }
let root = { Value = 1; Left = Some n2; Right = Some n3 }

let rec explore visit (node: Node) =
  visit node

  let exploreChild child =
    match child with
    | Some n -> explore visit n
    | _ -> ()

  exploreChild node.Left
  exploreChild node.Right

let displayNode (node: Node) =
  Console.writeLine $"{node.Value}"

explore displayNode root

5) Imports + exports (multi-file)

This example introduces:

  • import "..." as Alias,
  • top-level export let bindings,
  • using imported record types in exported functions.

File: includes/common.fss

type ProjectInfo = { Name: string; Language: string }

let describe_project project =
  $"{project.Name} ({project.Language})"

let describe_command cmd =
  $"step={cmd}"

let join_with_comma items =
  String.concat ", " items

File: imports-and-exports.fss

import "includes/common.fss" as Common

export let extension_name = "demo"

export let summary (project: Common.ProjectInfo) =
  let steps = [ "build"; "test"; "publish" ] |> List.map Common.describe_command
  $"{Common.describe_project project}: {Common.join_with_comma steps}"

let main =
  let project = { Name = "Terrabuild"; Language = "F#" }
  Console.writeLine (summary project)
  Console.writeLine $"Extension: {extension_name}"

main

6) An embedded pricing rule

This example shows the full product-facing loop: keep a business rule in FScript, export one intentional entry point, and invoke it from a JavaScript host.

File: pricing.fss

type Customer =
  { Plan: string
    Seats: int }

export let quote (customer: Customer) =
  let pricePerSeat =
    match customer.Plan with
    | "scale" -> 29
    | "team" -> 19
    | _ -> 9

  pricePerSeat * customer.Seats

File: pricing.mjs

import { readFile } from "node:fs/promises";
import { load, invoke } from "@magnusopera/fscript";

const source = await readFile(new URL("./pricing.fss", import.meta.url), "utf8");
const script = load(source);
const result = invoke(script, "quote", [{ Plan: "scale", Seats: 12 }]);

console.log(result.value); // 348n

Install the package and run the host:

npm install @magnusopera/fscript
node pricing.mjs

The script owns the changeable rule. The host owns loading, input, output, and every external capability. For typed extern functions and production concerns, continue with the Fable and JavaScript host guide.

Suggested order

  1. Run example 1 and 2 to get comfortable with flow and syntax.
  2. Run example 3 to practice pattern matching on real data.
  3. Run example 4 to practice recursion with typed data.
  4. Run example 5 when you are ready for multi-file scripts and host-facing exports.
  5. Run example 6 when you are ready to cross the script/host boundary.
  6. Run the BASIC sample under samples/basic/ when you want to see FScript host a second language with PRINT, INPUT, jumps, and loops.

Continue with FFI Graphics — A Rotating Teapot to generate a 3D model in FScript and send geometry, camera, and lighting through a typed .NET provider.

Check FFI sharing across tasks

The task-sharing analysis samples contrast unsafe shared state, hidden function state, ordered access, and a non-pure but thread-safe counter. They use a small provider with no graphics or native dependencies.