Skip to main content

rexlang_typesystem/
prelude.rs

1use std::sync::OnceLock;
2
3use rexlang_ast::expr::{Decl, Program};
4use rexlang_lexer::Token;
5use rexlang_parser::Parser;
6use rexlang_util::GasMeter;
7
8use crate::{AdtDecl, BuiltinTypeId, Predicate, Scheme, Type, TypeError, TypeSystem};
9use rexlang_ast::expr::sym;
10
11fn inject_prelude_classes_and_instances(ts: &mut TypeSystem) -> Result<(), TypeError> {
12    let program = prelude_typeclasses_program()?;
13    for decl in &program.decls {
14        match decl {
15            Decl::Class(class_decl) => ts.inject_class_decl(class_decl)?,
16            Decl::Instance(inst_decl) => {
17                ts.inject_instance_decl(inst_decl)?;
18            }
19            Decl::Type(..) | Decl::Fn(..) | Decl::DeclareFn(..) | Decl::Import(..) => {}
20        }
21    }
22    Ok(())
23}
24
25pub fn prelude_typeclasses_program() -> Result<&'static Program, TypeError> {
26    static PROGRAM: OnceLock<Result<Program, String>> = OnceLock::new();
27    let parsed = PROGRAM.get_or_init(|| {
28        let source = include_str!("prelude_typeclasses.rex");
29        let tokens =
30            Token::tokenize(source).map_err(|e| format!("prelude_typeclasses: lex error: {e}"))?;
31        let mut parser = Parser::new(tokens);
32        match parser.parse_program(&mut GasMeter::default()) {
33            Ok(program) => Ok(program),
34            Err(errs) => {
35                let mut out = String::from("prelude_typeclasses: parse error:");
36                for err in errs {
37                    out.push_str(&format!("\n  {err}"));
38                }
39                Err(out)
40            }
41        }
42    });
43    match parsed {
44        Ok(program) => Ok(program),
45        Err(msg) => Err(TypeError::Internal(msg.clone())),
46    }
47}
48
49fn inject_prelude_primops(ts: &mut TypeSystem) {
50    // Rust-backed intrinsics used by `rexlang-typesystem/src/prelude_typeclasses.rex`.
51    //
52    // These intentionally carry no typeclass predicates. An instance method
53    // body should not need to assume the class it is defining.
54    let bool_ty = Type::builtin(BuiltinTypeId::Bool);
55    let i32_ty = Type::builtin(BuiltinTypeId::I32);
56    let string_ty = Type::builtin(BuiltinTypeId::String);
57
58    // Equality intrinsics.
59    //
60    // Note: we make these “math-style” monomorphic overloads. Each
61    // `prim_eq`/`prim_ne` implementation is tied to one concrete runtime type.
62    // This avoids a single universal `eq` routine that switches on types at
63    // runtime (harder to reason about, harder to optimize).
64    {
65        let eq_types = [
66            BuiltinTypeId::Bool,
67            BuiltinTypeId::U8,
68            BuiltinTypeId::U16,
69            BuiltinTypeId::U32,
70            BuiltinTypeId::U64,
71            BuiltinTypeId::I8,
72            BuiltinTypeId::I16,
73            BuiltinTypeId::I32,
74            BuiltinTypeId::I64,
75            BuiltinTypeId::F32,
76            BuiltinTypeId::F64,
77            BuiltinTypeId::String,
78            BuiltinTypeId::Uuid,
79            BuiltinTypeId::DateTime,
80        ];
81        for builtin in eq_types {
82            let t = Type::builtin(builtin);
83            ts.add_overload(
84                "prim_eq",
85                Scheme::new(
86                    vec![],
87                    vec![],
88                    Type::fun(t.clone(), Type::fun(t.clone(), bool_ty.clone())),
89                ),
90            );
91            ts.add_overload(
92                "prim_ne",
93                Scheme::new(
94                    vec![],
95                    vec![],
96                    Type::fun(t.clone(), Type::fun(t, bool_ty.clone())),
97                ),
98            );
99        }
100    }
101
102    // Array equality is implemented by the runtime (it needs to iterate without
103    // allocating) but it must respect `Eq a`, so the primitive calls `(==)` on
104    // elements rather than doing structural `Value` equality.
105    {
106        let a_tv = ts.supply.fresh(Some(sym("a")));
107        let a = Type::var(a_tv.clone());
108        let array_a = Type::app(Type::builtin(BuiltinTypeId::Array), a.clone());
109        ts.add_value(
110            "prim_array_eq",
111            Scheme::new(
112                vec![a_tv.clone()],
113                vec![],
114                Type::fun(array_a.clone(), Type::fun(array_a.clone(), bool_ty.clone())),
115            ),
116        );
117        ts.add_value(
118            "prim_array_ne",
119            Scheme::new(
120                vec![a_tv],
121                vec![],
122                Type::fun(array_a.clone(), Type::fun(array_a, bool_ty.clone())),
123            ),
124        );
125    }
126
127    // Numeric intrinsics (monomorphic overloads).
128    {
129        let additive = [
130            BuiltinTypeId::String,
131            BuiltinTypeId::U8,
132            BuiltinTypeId::U16,
133            BuiltinTypeId::U32,
134            BuiltinTypeId::U64,
135            BuiltinTypeId::I8,
136            BuiltinTypeId::I16,
137            BuiltinTypeId::I32,
138            BuiltinTypeId::I64,
139            BuiltinTypeId::F32,
140            BuiltinTypeId::F64,
141        ];
142        for builtin in additive {
143            let t = Type::builtin(builtin);
144            ts.add_overload("prim_zero", Scheme::new(vec![], vec![], t.clone()));
145            ts.add_overload(
146                "prim_add",
147                Scheme::new(
148                    vec![],
149                    vec![],
150                    Type::fun(t.clone(), Type::fun(t.clone(), t.clone())),
151                ),
152            );
153        }
154
155        let multiplicative = [
156            BuiltinTypeId::U8,
157            BuiltinTypeId::U16,
158            BuiltinTypeId::U32,
159            BuiltinTypeId::U64,
160            BuiltinTypeId::I8,
161            BuiltinTypeId::I16,
162            BuiltinTypeId::I32,
163            BuiltinTypeId::I64,
164            BuiltinTypeId::F32,
165            BuiltinTypeId::F64,
166        ];
167        for builtin in multiplicative {
168            let t = Type::builtin(builtin);
169            ts.add_overload("prim_one", Scheme::new(vec![], vec![], t.clone()));
170            ts.add_overload(
171                "prim_mul",
172                Scheme::new(
173                    vec![],
174                    vec![],
175                    Type::fun(t.clone(), Type::fun(t.clone(), t.clone())),
176                ),
177            );
178        }
179
180        let signed = [
181            BuiltinTypeId::I8,
182            BuiltinTypeId::I16,
183            BuiltinTypeId::I32,
184            BuiltinTypeId::I64,
185            BuiltinTypeId::F32,
186            BuiltinTypeId::F64,
187        ];
188        for builtin in signed {
189            let t = Type::builtin(builtin);
190            ts.add_overload(
191                "prim_sub",
192                Scheme::new(
193                    vec![],
194                    vec![],
195                    Type::fun(t.clone(), Type::fun(t.clone(), t.clone())),
196                ),
197            );
198            ts.add_overload(
199                "prim_negate",
200                Scheme::new(vec![], vec![], Type::fun(t.clone(), t.clone())),
201            );
202        }
203
204        for builtin in [BuiltinTypeId::F32, BuiltinTypeId::F64] {
205            let t = Type::builtin(builtin);
206            ts.add_overload(
207                "prim_div",
208                Scheme::new(
209                    vec![],
210                    vec![],
211                    Type::fun(t.clone(), Type::fun(t.clone(), t.clone())),
212                ),
213            );
214        }
215
216        let integral = [
217            BuiltinTypeId::U8,
218            BuiltinTypeId::U16,
219            BuiltinTypeId::U32,
220            BuiltinTypeId::U64,
221            BuiltinTypeId::I8,
222            BuiltinTypeId::I16,
223            BuiltinTypeId::I32,
224            BuiltinTypeId::I64,
225        ];
226        for builtin in integral {
227            let t = Type::builtin(builtin);
228            ts.add_overload(
229                "prim_mod",
230                Scheme::new(
231                    vec![],
232                    vec![],
233                    Type::fun(t.clone(), Type::fun(t.clone(), t.clone())),
234                ),
235            );
236        }
237    }
238
239    // Ordering intrinsics (monomorphic overloads).
240    {
241        let ord = [
242            BuiltinTypeId::U8,
243            BuiltinTypeId::U16,
244            BuiltinTypeId::U32,
245            BuiltinTypeId::U64,
246            BuiltinTypeId::I8,
247            BuiltinTypeId::I16,
248            BuiltinTypeId::I32,
249            BuiltinTypeId::I64,
250            BuiltinTypeId::F32,
251            BuiltinTypeId::F64,
252            BuiltinTypeId::String,
253        ];
254        for builtin in ord {
255            let t = Type::builtin(builtin);
256            ts.add_overload(
257                "prim_cmp",
258                Scheme::new(
259                    vec![],
260                    vec![],
261                    Type::fun(t.clone(), Type::fun(t.clone(), i32_ty.clone())),
262                ),
263            );
264            for name in ["prim_lt", "prim_le", "prim_gt", "prim_ge"] {
265                ts.add_overload(
266                    name,
267                    Scheme::new(
268                        vec![],
269                        vec![],
270                        Type::fun(t.clone(), Type::fun(t.clone(), bool_ty.clone())),
271                    ),
272                );
273            }
274        }
275    }
276
277    // Show-printing intrinsics (monomorphic overloads).
278    {
279        let show_types = [
280            BuiltinTypeId::Bool,
281            BuiltinTypeId::U8,
282            BuiltinTypeId::U16,
283            BuiltinTypeId::U32,
284            BuiltinTypeId::U64,
285            BuiltinTypeId::I8,
286            BuiltinTypeId::I16,
287            BuiltinTypeId::I32,
288            BuiltinTypeId::I64,
289            BuiltinTypeId::F32,
290            BuiltinTypeId::F64,
291            BuiltinTypeId::String,
292            BuiltinTypeId::Uuid,
293            BuiltinTypeId::DateTime,
294        ];
295        for builtin in show_types {
296            let t = Type::builtin(builtin);
297            ts.add_overload(
298                "prim_show",
299                Scheme::new(vec![], vec![], Type::fun(t, string_ty.clone())),
300            );
301        }
302    }
303
304    // JSON stringification (used by `std.json`'s `Show` instance).
305    //
306    // This is intentionally a `prim_` helper with a polymorphic type so the
307    // `std.json` library can stay purely-Rex at the surface level.
308    {
309        let a_tv = ts.supply.fresh(Some(sym("a")));
310        let a = Type::var(a_tv.clone());
311        ts.add_value(
312            "prim_json_stringify",
313            Scheme::new(vec![a_tv], vec![], Type::fun(a, string_ty.clone())),
314        );
315    }
316
317    // prim_json_parse : string -> Result a string
318    //
319    // The ok type is polymorphic so `std.json` can instantiate it as
320    // `Result std.json.Value string` (and then wrap the string error into
321    // `DecodeError`).
322    {
323        let a_tv = ts.supply.fresh(Some(sym("a")));
324        let a = Type::var(a_tv.clone());
325        let result_con = Type::builtin(BuiltinTypeId::Result);
326        let result_as = Type::app(Type::app(result_con, string_ty.clone()), a);
327        ts.add_value(
328            "prim_json_parse",
329            Scheme::new(vec![a_tv], vec![], Type::fun(string_ty.clone(), result_as)),
330        );
331    }
332
333    // Collection intrinsics used by the standard type class instances.
334    //
335    // These are all `prim_` because they are the host-provided “bottom layer”.
336    // The user-facing API is the class methods (`map`, `foldl`, `zip`, `get`, ...).
337    {
338        let list_con = Type::builtin(BuiltinTypeId::List);
339        let array_con = Type::builtin(BuiltinTypeId::Array);
340        let option_con = Type::builtin(BuiltinTypeId::Option);
341        let result_con = Type::builtin(BuiltinTypeId::Result);
342
343        let list_of = |t: Type| Type::app(list_con.clone(), t);
344        let array_of = |t: Type| Type::app(array_con.clone(), t);
345        let option_of = |t: Type| Type::app(option_con.clone(), t);
346        let result_of = |ok: Type, err: Type| Type::app(Type::app(result_con.clone(), err), ok);
347
348        // prim_map
349        {
350            let a_tv = ts.supply.fresh(Some(sym("a")));
351            let b_tv = ts.supply.fresh(Some(sym("b")));
352            let a = Type::var(a_tv.clone());
353            let b = Type::var(b_tv.clone());
354            ts.add_overload(
355                "prim_map",
356                Scheme::new(
357                    vec![a_tv.clone(), b_tv.clone()],
358                    vec![],
359                    Type::fun(
360                        Type::fun(a.clone(), b.clone()),
361                        Type::fun(list_of(a.clone()), list_of(b.clone())),
362                    ),
363                ),
364            );
365            ts.add_overload(
366                "prim_map",
367                Scheme::new(
368                    vec![a_tv.clone(), b_tv.clone()],
369                    vec![],
370                    Type::fun(
371                        Type::fun(a.clone(), b.clone()),
372                        Type::fun(array_of(a.clone()), array_of(b.clone())),
373                    ),
374                ),
375            );
376            ts.add_overload(
377                "prim_map",
378                Scheme::new(
379                    vec![a_tv.clone(), b_tv.clone()],
380                    vec![],
381                    Type::fun(
382                        Type::fun(a.clone(), b.clone()),
383                        Type::fun(option_of(a.clone()), option_of(b.clone())),
384                    ),
385                ),
386            );
387            let e_tv = ts.supply.fresh(Some(sym("e")));
388            let e = Type::var(e_tv.clone());
389            ts.add_overload(
390                "prim_map",
391                Scheme::new(
392                    vec![a_tv, b_tv, e_tv],
393                    vec![],
394                    Type::fun(
395                        Type::fun(a.clone(), b.clone()),
396                        Type::fun(result_of(a.clone(), e.clone()), result_of(b.clone(), e)),
397                    ),
398                ),
399            );
400        }
401
402        // prim_array_singleton
403        {
404            let a_tv = ts.supply.fresh(Some(sym("a")));
405            let a = Type::var(a_tv.clone());
406            ts.add_value(
407                "prim_array_singleton",
408                Scheme::new(vec![a_tv], vec![], Type::fun(a.clone(), array_of(a))),
409            );
410        }
411
412        // prim_foldl / prim_foldr / prim_fold
413        {
414            let a_tv = ts.supply.fresh(Some(sym("a")));
415            let b_tv = ts.supply.fresh(Some(sym("b")));
416            let a = Type::var(a_tv.clone());
417            let b = Type::var(b_tv.clone());
418            let step_l = Type::fun(b.clone(), Type::fun(a.clone(), b.clone()));
419            let step_r = Type::fun(a.clone(), Type::fun(b.clone(), b.clone()));
420            let mut add_for = |fa: Type| {
421                ts.add_overload(
422                    "prim_foldl",
423                    Scheme::new(
424                        vec![a_tv.clone(), b_tv.clone()],
425                        vec![],
426                        Type::fun(
427                            step_l.clone(),
428                            Type::fun(b.clone(), Type::fun(fa.clone(), b.clone())),
429                        ),
430                    ),
431                );
432                ts.add_overload(
433                    "prim_foldr",
434                    Scheme::new(
435                        vec![a_tv.clone(), b_tv.clone()],
436                        vec![],
437                        Type::fun(
438                            step_r.clone(),
439                            Type::fun(b.clone(), Type::fun(fa.clone(), b.clone())),
440                        ),
441                    ),
442                );
443                ts.add_overload(
444                    "prim_fold",
445                    Scheme::new(
446                        vec![a_tv.clone(), b_tv.clone()],
447                        vec![],
448                        Type::fun(
449                            step_l.clone(),
450                            Type::fun(b.clone(), Type::fun(fa, b.clone())),
451                        ),
452                    ),
453                );
454            };
455
456            add_for(list_of(a.clone()));
457            add_for(array_of(a.clone()));
458            add_for(option_of(a.clone()));
459        }
460
461        // prim_filter / prim_filter_map
462        {
463            let a_tv = ts.supply.fresh(Some(sym("a")));
464            let b_tv = ts.supply.fresh(Some(sym("b")));
465            let a = Type::var(a_tv.clone());
466            let b = Type::var(b_tv.clone());
467            let pred = Type::fun(a.clone(), bool_ty.clone());
468            let mapper = Type::fun(a.clone(), option_of(b.clone()));
469            let mut add_for = |fa: Type, fb: Type| {
470                ts.add_overload(
471                    "prim_filter",
472                    Scheme::new(
473                        vec![a_tv.clone()],
474                        vec![],
475                        Type::fun(pred.clone(), Type::fun(fa.clone(), fa.clone())),
476                    ),
477                );
478                ts.add_overload(
479                    "prim_filter_map",
480                    Scheme::new(
481                        vec![a_tv.clone(), b_tv.clone()],
482                        vec![],
483                        Type::fun(mapper.clone(), Type::fun(fa, fb)),
484                    ),
485                );
486            };
487
488            add_for(list_of(a.clone()), list_of(b.clone()));
489            add_for(array_of(a.clone()), array_of(b.clone()));
490            add_for(option_of(a.clone()), option_of(b.clone()));
491        }
492
493        // prim_flat_map
494        {
495            // List / Array / Option
496            let a_tv = ts.supply.fresh(Some(sym("a")));
497            let b_tv = ts.supply.fresh(Some(sym("b")));
498            let a = Type::var(a_tv.clone());
499            let b = Type::var(b_tv.clone());
500            let mut add_for = |fa: Type, fb: Type| {
501                ts.add_overload(
502                    "prim_flat_map",
503                    Scheme::new(
504                        vec![a_tv.clone(), b_tv.clone()],
505                        vec![],
506                        Type::fun(Type::fun(a.clone(), fb.clone()), Type::fun(fa, fb)),
507                    ),
508                );
509            };
510
511            add_for(list_of(a.clone()), list_of(b.clone()));
512            add_for(array_of(a.clone()), array_of(b.clone()));
513            add_for(option_of(a.clone()), option_of(b.clone()));
514
515            // Result e
516            let e_tv = ts.supply.fresh(Some(sym("e")));
517            let e = Type::var(e_tv.clone());
518            let ra = result_of(a.clone(), e.clone());
519            let rb = result_of(b.clone(), e.clone());
520            ts.add_overload(
521                "prim_flat_map",
522                Scheme::new(
523                    vec![a_tv, b_tv, e_tv],
524                    vec![],
525                    Type::fun(Type::fun(a.clone(), rb.clone()), Type::fun(ra, rb)),
526                ),
527            );
528        }
529
530        // prim_or_else
531        {
532            let a_tv = ts.supply.fresh(Some(sym("a")));
533            let a = Type::var(a_tv.clone());
534            let mut add_for = |fa: Type| {
535                let fa2 = fa.clone();
536                ts.add_overload(
537                    "prim_or_else",
538                    Scheme::new(
539                        vec![a_tv.clone()],
540                        vec![],
541                        Type::fun(Type::fun(fa.clone(), fa.clone()), Type::fun(fa2, fa)),
542                    ),
543                );
544            };
545
546            add_for(list_of(a.clone()));
547            add_for(array_of(a.clone()));
548            add_for(option_of(a.clone()));
549
550            let e_tv = ts.supply.fresh(Some(sym("e")));
551            let e = Type::var(e_tv.clone());
552            let ra = result_of(a.clone(), e);
553            ts.add_overload(
554                "prim_or_else",
555                Scheme::new(
556                    vec![a_tv, e_tv],
557                    vec![],
558                    Type::fun(Type::fun(ra.clone(), ra.clone()), Type::fun(ra.clone(), ra)),
559                ),
560            );
561        }
562
563        // prim_take / prim_skip
564        {
565            let a_tv = ts.supply.fresh(Some(sym("a")));
566            let a = Type::var(a_tv.clone());
567            let mut add_for = |fa: Type| {
568                let scheme = Scheme::new(
569                    vec![a_tv.clone()],
570                    vec![],
571                    Type::fun(i32_ty.clone(), Type::fun(fa.clone(), fa)),
572                );
573                ts.add_overload("prim_take", scheme.clone());
574                ts.add_overload("prim_skip", scheme);
575            };
576            add_for(list_of(a.clone()));
577            add_for(array_of(a.clone()));
578        }
579
580        // prim_zip / prim_unzip
581        {
582            let a_tv = ts.supply.fresh(Some(sym("a")));
583            let b_tv = ts.supply.fresh(Some(sym("b")));
584            let a = Type::var(a_tv.clone());
585            let b = Type::var(b_tv.clone());
586            let pair = Type::tuple(vec![a.clone(), b.clone()]);
587            let mut add_for = |fa: Type, fb: Type, fp: Type| {
588                ts.add_overload(
589                    "prim_zip",
590                    Scheme::new(
591                        vec![a_tv.clone(), b_tv.clone()],
592                        vec![],
593                        Type::fun(fa.clone(), Type::fun(fb.clone(), fp.clone())),
594                    ),
595                );
596                ts.add_overload(
597                    "prim_unzip",
598                    Scheme::new(
599                        vec![a_tv.clone(), b_tv.clone()],
600                        vec![],
601                        Type::fun(fp, Type::tuple(vec![fa, fb])),
602                    ),
603                );
604            };
605
606            add_for(
607                list_of(a.clone()),
608                list_of(b.clone()),
609                list_of(pair.clone()),
610            );
611            add_for(array_of(a.clone()), array_of(b.clone()), array_of(pair));
612        }
613
614        // prim_get
615        {
616            let a_tv = ts.supply.fresh(Some(sym("a")));
617            let a = Type::var(a_tv.clone());
618            let idx = i32_ty.clone();
619            ts.add_overload(
620                "prim_get",
621                Scheme::new(
622                    vec![a_tv.clone()],
623                    vec![],
624                    Type::fun(idx.clone(), Type::fun(list_of(a.clone()), a.clone())),
625                ),
626            );
627            ts.add_overload(
628                "prim_get",
629                Scheme::new(
630                    vec![a_tv.clone()],
631                    vec![],
632                    Type::fun(idx.clone(), Type::fun(array_of(a.clone()), a.clone())),
633                ),
634            );
635            for size in 2..=32 {
636                ts.add_overload(
637                    "prim_get",
638                    Scheme::new(
639                        vec![a_tv.clone()],
640                        vec![],
641                        Type::fun(
642                            idx.clone(),
643                            Type::fun(Type::tuple(vec![a.clone(); size]), a.clone()),
644                        ),
645                    ),
646                );
647            }
648        }
649
650        // List/Array conversion helpers.
651        {
652            let a_tv = ts.supply.fresh(Some(sym("a")));
653            let a = Type::var(a_tv.clone());
654            let list_a = list_of(a.clone());
655            let array_a = array_of(a.clone());
656            ts.add_value(
657                "prim_array_from_list",
658                Scheme::new(
659                    vec![a_tv.clone()],
660                    vec![],
661                    Type::fun(list_a.clone(), array_a.clone()),
662                ),
663            );
664            ts.add_value(
665                "prim_list_from_array",
666                Scheme::new(
667                    vec![a_tv.clone()],
668                    vec![],
669                    Type::fun(array_a.clone(), list_a.clone()),
670                ),
671            );
672            ts.add_value(
673                "to_array",
674                Scheme::new(
675                    vec![a_tv.clone()],
676                    vec![],
677                    Type::fun(list_a.clone(), array_a.clone()),
678                ),
679            );
680            ts.add_value(
681                "to_list",
682                Scheme::new(vec![a_tv], vec![], Type::fun(array_a, list_a)),
683            );
684        }
685
686        // prim_dict_map : (a -> b) -> Dict a -> Dict b
687        {
688            let a_tv = ts.supply.fresh(Some(sym("a")));
689            let b_tv = ts.supply.fresh(Some(sym("b")));
690            let a = Type::var(a_tv.clone());
691            let b = Type::var(b_tv.clone());
692            let dict_a = Type::app(Type::builtin(BuiltinTypeId::Dict), a.clone());
693            let dict_b = Type::app(Type::builtin(BuiltinTypeId::Dict), b.clone());
694            ts.add_value(
695                "prim_dict_map",
696                Scheme::new(
697                    vec![a_tv, b_tv],
698                    vec![],
699                    Type::fun(Type::fun(a, b), Type::fun(dict_a, dict_b)),
700                ),
701            );
702        }
703
704        // prim_dict_traverse_result : (a -> Result b e) -> Dict a -> Result (Dict b) e
705        {
706            let a_tv = ts.supply.fresh(Some(sym("a")));
707            let b_tv = ts.supply.fresh(Some(sym("b")));
708            let e_tv = ts.supply.fresh(Some(sym("e")));
709            let a = Type::var(a_tv.clone());
710            let b = Type::var(b_tv.clone());
711            let e = Type::var(e_tv.clone());
712            let dict_a = Type::app(Type::builtin(BuiltinTypeId::Dict), a.clone());
713            let dict_b = Type::app(Type::builtin(BuiltinTypeId::Dict), b.clone());
714            let result_eb = result_of(b.clone(), e.clone());
715            let result_edictb = result_of(dict_b, e);
716            ts.add_value(
717                "prim_dict_traverse_result",
718                Scheme::new(
719                    vec![a_tv, b_tv, e_tv],
720                    vec![],
721                    Type::fun(Type::fun(a, result_eb), Type::fun(dict_a, result_edictb)),
722                ),
723            );
724        }
725
726        // Numeric conversions used by `std.json`.
727        //
728        // We model these as primitive intrinsics to keep Rex code simple and to
729        // make overflow/rounding rules explicit at the host boundary.
730        for src in [
731            BuiltinTypeId::U8,
732            BuiltinTypeId::U16,
733            BuiltinTypeId::U32,
734            BuiltinTypeId::U64,
735            BuiltinTypeId::I8,
736            BuiltinTypeId::I16,
737            BuiltinTypeId::I32,
738            BuiltinTypeId::I64,
739            BuiltinTypeId::F32,
740            BuiltinTypeId::F64,
741        ] {
742            let t = Type::builtin(src);
743            ts.add_overload(
744                "prim_to_f64",
745                Scheme::new(
746                    vec![],
747                    vec![],
748                    Type::fun(t, Type::builtin(BuiltinTypeId::F64)),
749                ),
750            );
751        }
752
753        for (name, dst) in [
754            ("prim_f64_to_u8", BuiltinTypeId::U8),
755            ("prim_f64_to_u16", BuiltinTypeId::U16),
756            ("prim_f64_to_u32", BuiltinTypeId::U32),
757            ("prim_f64_to_u64", BuiltinTypeId::U64),
758            ("prim_f64_to_i8", BuiltinTypeId::I8),
759            ("prim_f64_to_i16", BuiltinTypeId::I16),
760            ("prim_f64_to_i32", BuiltinTypeId::I32),
761            ("prim_f64_to_i64", BuiltinTypeId::I64),
762            ("prim_f64_to_f32", BuiltinTypeId::F32),
763        ] {
764            let dst_ty = Type::builtin(dst);
765            ts.add_value(
766                name,
767                Scheme::new(
768                    vec![],
769                    vec![],
770                    Type::fun(Type::builtin(BuiltinTypeId::F64), option_of(dst_ty)),
771                ),
772            );
773        }
774
775        ts.add_value(
776            "prim_parse_uuid",
777            Scheme::new(
778                vec![],
779                vec![],
780                Type::fun(
781                    Type::builtin(BuiltinTypeId::String),
782                    option_of(Type::builtin(BuiltinTypeId::Uuid)),
783                ),
784            ),
785        );
786        ts.add_value(
787            "prim_parse_datetime",
788            Scheme::new(
789                vec![],
790                vec![],
791                Type::fun(
792                    Type::builtin(BuiltinTypeId::String),
793                    option_of(Type::builtin(BuiltinTypeId::DateTime)),
794                ),
795            ),
796        );
797    }
798}
799
800pub(crate) fn build_prelude(ts: &mut TypeSystem) -> Result<(), TypeError> {
801    // Primitive type constructors
802    let prims = [
803        BuiltinTypeId::U8,
804        BuiltinTypeId::U16,
805        BuiltinTypeId::U32,
806        BuiltinTypeId::U64,
807        BuiltinTypeId::I8,
808        BuiltinTypeId::I16,
809        BuiltinTypeId::I32,
810        BuiltinTypeId::I64,
811        BuiltinTypeId::F32,
812        BuiltinTypeId::F64,
813        BuiltinTypeId::Bool,
814        BuiltinTypeId::String,
815        BuiltinTypeId::Uuid,
816        BuiltinTypeId::DateTime,
817    ];
818    for prim in prims {
819        ts.env.extend(
820            prim.as_symbol(),
821            Scheme::new(vec![], vec![], Type::builtin(prim)),
822        );
823    }
824
825    // Type constructors for ADTs used in prelude schemes.
826    let result_con = Type::builtin(BuiltinTypeId::Result);
827    let option_con = Type::builtin(BuiltinTypeId::Option);
828
829    // Register ADT constructors as value-level functions.
830    {
831        let list_name = sym("List");
832        let a_name = sym("a");
833        let list_params = vec![a_name.clone()];
834        let mut list_adt = AdtDecl::new(&list_name, &list_params, &mut ts.supply);
835        let a = list_adt.param_type(&a_name).ok_or_else(|| {
836            TypeError::Internal("prelude: List is missing type parameter `a`".into())
837        })?;
838        let list_a = list_adt.result_type();
839        list_adt.add_variant(sym("Empty"), vec![]);
840        list_adt.add_variant(sym("Cons"), vec![a.clone(), list_a.clone()]);
841        ts.inject_adt(&list_adt);
842    }
843    {
844        let option_name = sym("Option");
845        let t_name = sym("t");
846        let option_params = vec![t_name.clone()];
847        let mut option_adt = AdtDecl::new(&option_name, &option_params, &mut ts.supply);
848        let t = option_adt.param_type(&t_name).ok_or_else(|| {
849            TypeError::Internal("prelude: Option is missing type parameter `t`".into())
850        })?;
851        option_adt.add_variant(sym("Some"), vec![t]);
852        option_adt.add_variant(sym("None"), vec![]);
853        ts.inject_adt(&option_adt);
854    }
855    {
856        let result_name = sym("Result");
857        let e_name = sym("e");
858        let t_name = sym("t");
859        let result_params = vec![e_name.clone(), t_name.clone()];
860        let mut result_adt = AdtDecl::new(&result_name, &result_params, &mut ts.supply);
861        let e = result_adt.param_type(&e_name).ok_or_else(|| {
862            TypeError::Internal("prelude: Result is missing type parameter `e`".into())
863        })?;
864        let t = result_adt.param_type(&t_name).ok_or_else(|| {
865            TypeError::Internal("prelude: Result is missing type parameter `t`".into())
866        })?;
867        result_adt.add_variant(sym("Err"), vec![e]);
868        result_adt.add_variant(sym("Ok"), vec![t]);
869        ts.inject_adt(&result_adt);
870    }
871
872    inject_prelude_primops(ts);
873    inject_prelude_classes_and_instances(ts)?;
874
875    // Helper constructors used to describe prelude schemes below.
876    let fresh_tv = |ts: &mut TypeSystem, name: &str| ts.supply.fresh(Some(sym(name)));
877    let option_of = |t: Type| Type::app(option_con.clone(), t);
878    let result_of = |t: Type, e: Type| Type::app(Type::app(result_con.clone(), e), t);
879
880    // Inject provided function declarations and schemes.
881
882    // Boolean operators
883    let bool_ty = Type::builtin(BuiltinTypeId::Bool);
884    ts.add_value(
885        "&&",
886        Scheme::new(
887            vec![],
888            vec![],
889            Type::fun(bool_ty.clone(), Type::fun(bool_ty.clone(), bool_ty.clone())),
890        ),
891    );
892    ts.add_value(
893        "||",
894        Scheme::new(
895            vec![],
896            vec![],
897            Type::fun(bool_ty.clone(), Type::fun(bool_ty.clone(), bool_ty.clone())),
898        ),
899    );
900
901    // Collection helpers (type class based)
902    {
903        let f_tv = fresh_tv(ts, "f");
904        let a_tv = fresh_tv(ts, "a");
905        let f = Type::var(f_tv.clone());
906        let a = Type::var(a_tv.clone());
907        let fa = Type::app(f.clone(), a.clone());
908
909        ts.add_value(
910            "sum",
911            Scheme::new(
912                vec![f_tv.clone(), a_tv.clone()],
913                vec![
914                    Predicate::new("Foldable", f.clone()),
915                    Predicate::new("AdditiveMonoid", a.clone()),
916                ],
917                Type::fun(fa.clone(), a.clone()),
918            ),
919        );
920        ts.add_value(
921            "mean",
922            Scheme::new(
923                vec![f_tv.clone(), a_tv.clone()],
924                vec![
925                    Predicate::new("Foldable", f.clone()),
926                    Predicate::new("Field", a.clone()),
927                ],
928                Type::fun(fa.clone(), a.clone()),
929            ),
930        );
931        ts.add_value(
932            "count",
933            Scheme::new(
934                vec![f_tv.clone(), a_tv.clone()],
935                vec![Predicate::new("Foldable", f.clone())],
936                Type::fun(fa.clone(), Type::builtin(BuiltinTypeId::I32)),
937            ),
938        );
939        ts.add_value(
940            "min",
941            Scheme::new(
942                vec![f_tv.clone(), a_tv.clone()],
943                vec![
944                    Predicate::new("Foldable", f.clone()),
945                    Predicate::new("Ord", a.clone()),
946                ],
947                Type::fun(fa.clone(), a.clone()),
948            ),
949        );
950        ts.add_value(
951            "max",
952            Scheme::new(
953                vec![f_tv, a_tv],
954                vec![
955                    Predicate::new("Foldable", f.clone()),
956                    Predicate::new("Ord", a.clone()),
957                ],
958                Type::fun(fa.clone(), a.clone()),
959            ),
960        );
961    }
962
963    // Option helpers
964    {
965        let a_tv = fresh_tv(ts, "a");
966        let a = Type::var(a_tv.clone());
967        let opt_a = option_of(a.clone());
968        ts.add_value(
969            "is_some",
970            Scheme::new(
971                vec![a_tv.clone()],
972                vec![],
973                Type::fun(opt_a.clone(), bool_ty.clone()),
974            ),
975        );
976        ts.add_value(
977            "is_none",
978            Scheme::new(
979                vec![a_tv.clone()],
980                vec![],
981                Type::fun(opt_a.clone(), bool_ty.clone()),
982            ),
983        );
984    }
985
986    // Result helpers
987    {
988        let t_tv = fresh_tv(ts, "t");
989        let e_tv = fresh_tv(ts, "e");
990        let t = Type::var(t_tv.clone());
991        let e = Type::var(e_tv.clone());
992        let res_te = result_of(t.clone(), e.clone());
993        ts.add_value(
994            "is_ok",
995            Scheme::new(
996                vec![t_tv.clone(), e_tv.clone()],
997                vec![],
998                Type::fun(res_te.clone(), bool_ty.clone()),
999            ),
1000        );
1001        ts.add_value(
1002            "is_err",
1003            Scheme::new(
1004                vec![t_tv.clone(), e_tv.clone()],
1005                vec![],
1006                Type::fun(res_te.clone(), bool_ty.clone()),
1007            ),
1008        );
1009    }
1010
1011    Ok(())
1012}