Skip to main content

rex_typesystem/
prelude.rs

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