Skip to main content

rex_typesystem/
prelude.rs

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