Repository navigation
Expand file tree
/
Copy patheffects.mere
More file actions
77 lines (64 loc) · 2.95 KB
/
Copy patheffects.mere
File metadata and controls
77 lines (64 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Demo of the capability-passing pattern.
//
// Mere's effect system is designed around "side effect = pass a capability
// as a value argument" (see internal design notes 05_effect_system.md,
// 10_effect_trial_findings.md). It works on HM + function arguments alone,
// and effect information does not appear in types.
//
// This file demonstrates that cap-passing can be written using only the
// language's currently available features.
type 'a list = Nil | Cons of 'a * 'a list;
let rec iter = fn f -> fn xs -> match xs with
| [] -> ()
| [h, ...t] -> { f h; iter f t };
let rec map = fn f -> fn xs -> match xs with
| [] -> []
| [h, ...t] -> Cons (f h, map f t);
// === Capability types (builtin) ===
// `Logger` / `Metrics` are builtin cap types (pre-registered by typer/eval).
// Construct them with `mk_logger : str -> Logger` /
// `mk_metrics : unit -> Metrics`.
//
// Breakdown:
// Logger { info: str -> unit, warn: str -> unit, error: str -> unit }
// Metrics { inc: str -> unit, record: str -> int -> unit }
//
// User-defined caps follow the same pattern:
// `type MyCap = { ... }` + `let mk_mycap = fn -> ...`.
// === Pattern 1: use the cap directly in a first-order function ===
// Receive the cap as an argument and call it through .field on the call site.
// The signature makes it obvious at a glance that "this function uses
// Logger and Metrics".
let save_order = fn (logger: Logger) -> fn (metrics: Metrics) -> fn (order_id: int) -> {
logger.info ("saving order " ++ show order_id);
metrics.inc "orders.saved";
metrics.record "orders.id" order_id;
};
// === Pattern 2: bucket relay (intermediate function) ===
// process_user does not use metrics directly but receives it in order to
// pass it through to save_order. In Mere's philosophy this is not "a bug
// rather than a feature" but "visibility of the effect path".
let process_user =
fn (logger: Logger) -> fn (metrics: Metrics) ->
fn (user_id: int) -> fn (order_id: int) -> {
logger.info ("user " ++ show user_id ++ " processing");
save_order logger metrics order_id;
};
// === Pattern 3: close over caps with HOF + partial application ===
// A higher-order function like iter is cap-agnostic (generic). Functions
// that use caps are partially applied first to bake the caps in, then
// passed in. This is the answer to Q-003 / Q-006.
//
// The `using [cap]` sugar lifts caps up to outer curry params:
// `fn x using [logger] -> body` is equivalent to `fn logger -> fn x -> body`
// so `f cap` yields a `T -> U` with the cap baked in, easy to pass to HOFs.
let handle_orders =
fn order_ids using [logger: Logger, metrics: Metrics, user_id: int] ->
// Bake the caps in via partial app to produce an `int -> unit` for iter.
let handle_one = fn id -> process_user logger metrics user_id id in
iter handle_one order_ids;
// === Execution ===
let logger = mk_logger "app";
let metrics = mk_metrics ();
let orders = [101, 102, 103];
handle_orders logger metrics 42 orders