Skip to main content

rexlang_engine/libraries/
library.rs

1use futures::FutureExt;
2use rexlang_ast::expr::{Decl, NameRef, TypeDecl, TypeExpr, TypeVariant, intern, sym};
3use rexlang_lexer::span::Span;
4use rexlang_typesystem::{
5    AdtDecl, BuiltinTypeId, Predicate, Scheme, Type, TypeKind, TypeVar, collect_adts_in_types,
6};
7use rexlang_util::GasMeter;
8
9use crate::EvaluatorRef;
10use crate::engine::{AsyncHandler, Export, Handler, NativeFuture, apply, order_adt_family};
11use crate::evaluator::EvaluatorRef as RuntimeEvaluatorRef;
12use crate::{
13    CancellationToken, Engine, EngineError, FromPointer, IntoPointer, Pointer, ROOT_LIBRARY_NAME,
14    RexType, Value,
15};
16
17/// A staged host library that you build up in Rust and later inject into an [`Engine`].
18///
19/// `Library` is the host-side representation of a Rex module. It lets embedders collect:
20///
21/// - Rex declarations such as `pub type ...`
22/// - typed Rust handlers via [`Library::export`] / [`Library::export_async`]
23/// - pointer-level native handlers via [`Library::export_native`] /
24///   [`Library::export_native_async`]
25///
26/// Once the library is assembled, pass it to [`Engine::inject_library`] to make it importable
27/// from Rex code.
28///
29/// This type is intentionally mutable and staged: you can build it incrementally, inspect its
30/// raw and structured declarations plus [`Library::exports`], transform them, and only inject it
31/// once you are satisfied with the final module shape.
32///
33/// # Examples
34///
35/// ```rust,ignore
36/// use rexlang_engine::{Engine, Library};
37///
38/// let mut engine = Engine::with_prelude(()).unwrap();
39/// engine.add_default_resolvers();
40///
41/// let mut math = Library::new("acme.math");
42/// math.export("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
43/// math.add_raw_declaration("pub type Sign = Positive | Negative").unwrap();
44///
45/// engine.inject_library(math).unwrap();
46/// ```
47pub struct Library<State: Clone + Send + Sync + 'static> {
48    /// The module name Rex code will import.
49    ///
50    /// This should be the fully-qualified library path you want users to write in `import`
51    /// declarations, such as `acme.math` or `sample`.
52    ///
53    /// [`Engine::inject_library`] validates and reserves this name when the library is injected.
54    ///
55    /// # Examples
56    ///
57    /// ```rust,ignore
58    /// use rexlang_engine::Library;
59    ///
60    /// let library = Library::<()>::new("acme.math");
61    /// assert_eq!(library.name, "acme.math");
62    /// ```
63    pub name: String,
64
65    /// Raw Rex declarations supplied directly by the embedder.
66    ///
67    /// This is most commonly used for `pub type ...` declarations, but it can hold any raw Rex
68    /// declaration text you want included in the virtual module source.
69    ///
70    /// The usual way to append to this field is [`Library::add_raw_declaration`], which validates that
71    /// the added text is non-empty. The field itself is public so callers can inspect or construct
72    /// a library in multiple passes.
73    ///
74    /// # Examples
75    ///
76    /// ```rust,ignore
77    /// use rexlang_engine::Library;
78    ///
79    /// let mut library = Library::<()>::new("acme.status");
80    /// library
81    ///     .add_raw_declaration("pub type Status = Ready | Failed string")
82    ///     .unwrap();
83    ///
84    /// assert_eq!(library.raw_declarations.len(), 1);
85    /// ```
86    pub raw_declarations: Vec<String>,
87
88    /// Structured declarations registered from Rust metadata rather than Rex source.
89    ///
90    /// APIs such as [`Library::add_adt_decl`] and [`Library::add_rex_adt`] append here instead
91    /// of synthesizing Rex source text.
92    pub structured_decls: Vec<Decl>,
93
94    pub(crate) staged_adts: Vec<AdtDecl>,
95
96    /// Staged host exports that will become callable Rex values when the library is injected.
97    ///
98    /// Each [`Export`] bundles a public Rex name, a declaration that is inserted into the virtual
99    /// module source, and the runtime injector that registers the implementation with the engine.
100    ///
101    /// Most callers populate this with [`Library::export`], [`Library::export_async`],
102    /// [`Library::export_native`], [`Library::export_native_async`], or [`Library::add_export`].
103    /// The field is public so advanced embedders can construct exports separately and assemble the
104    /// final library programmatically.
105    ///
106    /// # Examples
107    ///
108    /// ```rust,ignore
109    /// use rexlang_engine::{Export, Library};
110    ///
111    /// let mut library = Library::<()>::new("acme.math");
112    /// let export = Export::from_handler("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
113    /// library.exports.push(export);
114    ///
115    /// assert_eq!(library.exports.len(), 1);
116    /// ```
117    pub exports: Vec<Export<State>>,
118}
119
120impl<State> Library<State>
121where
122    State: Clone + Send + Sync + 'static,
123{
124    fn tracing_log_scheme() -> Scheme {
125        let a_tv = TypeVar::new(0, Some(sym("a")));
126        let a = Type::var(a_tv.clone());
127        Scheme::new(
128            vec![a_tv],
129            vec![Predicate::new("Show", a.clone())],
130            Type::fun(a, Type::builtin(BuiltinTypeId::String)),
131        )
132    }
133
134    /// Create an empty staged library that targets the engine root namespace.
135    ///
136    /// Injecting a global library installs its declarations and exports directly
137    /// into the engine rather than making them importable as a named module.
138    pub fn global() -> Self {
139        Self::new(ROOT_LIBRARY_NAME)
140    }
141
142    /// Create an empty staged library with the given import name.
143    ///
144    /// The returned library contains no declarations and no exports yet. Add those with the
145    /// helper methods on `Library`, then pass it to [`Engine::inject_library`].
146    ///
147    /// # Examples
148    ///
149    /// ```rust,ignore
150    /// use rexlang_engine::Library;
151    ///
152    /// let library = Library::<()>::new("acme.math");
153    /// assert_eq!(library.name, "acme.math");
154    /// assert!(library.raw_declarations.is_empty());
155    /// assert!(library.exports.is_empty());
156    /// ```
157    pub fn new(name: impl Into<String>) -> Self {
158        Self {
159            name: name.into(),
160            raw_declarations: Vec::new(),
161            structured_decls: Vec::new(),
162            staged_adts: Vec::new(),
163            exports: Vec::new(),
164        }
165    }
166
167    /// Append a raw Rex declaration to this staged library.
168    ///
169    /// Use this when you already have declaration text in Rex syntax, for example `pub type ...`.
170    /// The text is stored exactly as provided and later concatenated into the virtual module source
171    /// that [`Engine::inject_library`] exposes to Rex imports.
172    ///
173    /// This rejects empty or whitespace-only strings.
174    ///
175    /// # Examples
176    ///
177    /// ```rust,ignore
178    /// use rexlang_engine::Library;
179    ///
180    /// let mut library = Library::<()>::new("acme.status");
181    /// library
182    ///     .add_raw_declaration("pub type Status = Ready | Failed string")
183    ///     .unwrap();
184    /// ```
185    pub fn add_raw_declaration(
186        &mut self,
187        declaration: impl Into<String>,
188    ) -> Result<(), EngineError> {
189        let declaration = declaration.into();
190        if declaration.trim().is_empty() {
191            return Err(EngineError::Internal(
192                "library declaration cannot be empty".into(),
193            ));
194        }
195        self.raw_declarations.push(declaration);
196        Ok(())
197    }
198
199    /// Append a structured Rex declaration to this staged library.
200    pub fn add_decl(&mut self, decl: Decl) {
201        self.structured_decls.push(decl);
202    }
203
204    /// Append multiple structured Rex declarations to this staged library.
205    pub fn add_decls(&mut self, decls: impl IntoIterator<Item = Decl>) {
206        self.structured_decls.extend(decls);
207    }
208
209    /// Convert an [`AdtDecl`] into a structured type declaration and append it to this library.
210    ///
211    /// This is a structured alternative to [`Library::add_raw_declaration`] when you already have
212    /// an ADT declaration in typed form.
213    ///
214    /// # Examples
215    ///
216    /// ```rust,ignore
217    /// use rexlang_engine::{Engine, Library};
218    ///
219    /// let mut engine = Engine::with_prelude(()).unwrap();
220    /// let mut library = Library::new("acme.types");
221    /// let adt = engine.adt_decl_from_type(&rexlang_typesystem::Type::user_con("Thing", 0)).unwrap();
222    ///
223    /// library.add_adt_decl(adt).unwrap();
224    /// ```
225    pub fn add_adt_decl(&mut self, adt: AdtDecl) -> Result<(), EngineError> {
226        self.add_adt_family(vec![adt])
227    }
228
229    /// Append an acyclic family of ADT declarations to this staged library.
230    ///
231    /// Families are ordered before insertion so declarations are staged in
232    /// dependency order, and cycles are rejected.
233    pub fn add_adt_family(&mut self, adts: Vec<AdtDecl>) -> Result<(), EngineError> {
234        for adt in order_adt_family(adts)? {
235            let candidate = type_decl_from_adt(&adt);
236            let already_staged = self.structured_decls.iter().find_map(|decl| match decl {
237                Decl::Type(type_decl) if type_decl.name == adt.name => Some(type_decl),
238                _ => None,
239            });
240            if let Some(existing_decl) = already_staged {
241                if existing_decl != &candidate {
242                    return Err(EngineError::Custom(format!(
243                        "conflicting staged ADT registration for `{}`: existing declaration differs from new ADT declaration",
244                        adt.name,
245                    )));
246                }
247                continue;
248            }
249            self.staged_adts.push(adt);
250            self.structured_decls.push(Decl::Type(candidate));
251        }
252        Ok(())
253    }
254
255    /// Discover user ADTs referenced by the supplied types and append their declarations.
256    ///
257    /// This is useful when you have Rust-side type information and want to register the
258    /// corresponding user-defined ADTs for every type it mentions.
259    ///
260    /// The discovery process:
261    ///
262    /// - walks the provided types recursively
263    /// - deduplicates repeated ADTs
264    /// - asks the engine to materialize each discovered ADT declaration
265    /// - appends the resulting structured declarations to this library
266    ///
267    /// If conflicting ADT definitions are found for the same type constructor name, this returns
268    /// an [`EngineError`] that describes the conflict instead of silently picking one.
269    ///
270    /// # Examples
271    ///
272    /// ```rust,ignore
273    /// use rex_engine::{Engine, Library};
274    /// use rexlang_typesystem::{BuiltinTypeId, Type};
275    ///
276    /// let mut engine = Engine::with_prelude(()).unwrap();
277    /// let mut library = Library::new("acme.types");
278    /// let types = vec![
279    ///     Type::app(Type::user_con("Foo", 1), Type::builtin(BuiltinTypeId::I32)),
280    ///     Type::user_con("Bar", 0),
281    /// ];
282    ///
283    /// library.add_adt_decls_from_types(&mut engine, types).unwrap();
284    /// ```
285    pub fn add_adt_decls_from_types(
286        &mut self,
287        engine: &mut Engine<State>,
288        types: Vec<Type>,
289    ) -> Result<(), EngineError> {
290        let adts = collect_adts_in_types(types).map_err(crate::collect_adts_error_to_engine)?;
291        for typ in adts {
292            let adt = engine.adt_decl_from_type(&typ)?;
293            self.add_adt_decl(adt)?;
294        }
295        Ok(())
296    }
297
298    /// Derive a Rex ADT declaration from a Rust type and append it to this library.
299    ///
300    /// This is the most ergonomic way to expose a Rust enum or struct that implements [`RexType`]
301    /// as a library-local structured Rex type declaration.
302    ///
303    /// Unlike older engine-level registration helpers, this stages the declaration
304    /// inside the library so the caller can choose whether to inject it globally or
305    /// as a named module.
306    ///
307    /// # Examples
308    ///
309    /// ```rust,ignore
310    /// use rexlang_engine::{Engine, Library};
311    ///
312    /// #[derive(rexlang::Rex)]
313    /// struct Label {
314    ///     text: String,
315    /// }
316    ///
317    /// let mut engine = Engine::with_prelude(()).unwrap();
318    /// let mut library = Library::new("sample");
319    /// library.add_rex_adt::<Label>().unwrap();
320    /// ```
321    pub fn add_rex_adt<T>(&mut self) -> Result<(), EngineError>
322    where
323        T: RexType,
324    {
325        let mut family = Vec::new();
326        T::collect_rex_family(&mut family)?;
327        self.add_adt_family(family)
328    }
329
330    /// Append a preconstructed [`Export`] to this library.
331    ///
332    /// This is useful when exports are assembled elsewhere, such as from plugin metadata or a
333    /// higher-level registration layer.
334    ///
335    /// # Examples
336    ///
337    /// ```rust,ignore
338    /// use rexlang_engine::{Export, Library};
339    ///
340    /// let mut library = Library::<()>::new("acme.math");
341    /// let export = Export::from_handler("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
342    /// library.add_export(export);
343    /// ```
344    pub fn add_export(&mut self, export: Export<State>) {
345        self.exports.push(export);
346    }
347
348    /// Stage a typed synchronous Rust handler as a library export.
349    ///
350    /// This is the most convenient API for exporting ordinary Rust functions or closures into a
351    /// library. The handler's argument and return types drive the Rex signature automatically.
352    ///
353    /// The staged export becomes available to Rex code after [`Engine::inject_library`] is called.
354    ///
355    /// # Examples
356    ///
357    /// ```rust,ignore
358    /// use rexlang_engine::Library;
359    ///
360    /// let mut library = Library::<()>::new("acme.math");
361    /// library.export("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
362    /// ```
363    pub fn export<Sig, H>(&mut self, name: impl Into<String>, handler: H) -> Result<(), EngineError>
364    where
365        H: Handler<State, Sig>,
366    {
367        self.exports.push(Export::from_handler(name, handler)?);
368        Ok(())
369    }
370
371    /// Stage a typed asynchronous Rust handler as a library export.
372    ///
373    /// Use this when the host implementation is naturally async, for example when it awaits I/O or
374    /// other long-running work.
375    ///
376    /// # Examples
377    ///
378    /// ```rust,ignore
379    /// use rexlang_engine::Library;
380    ///
381    /// let mut library = Library::<()>::new("acme.math");
382    /// library
383    ///     .export_async("double_async", |_state: &(), x: i32| async move { Ok(x * 2) })
384    ///     .unwrap();
385    /// ```
386    pub fn export_async<Sig, H>(
387        &mut self,
388        name: impl Into<String>,
389        handler: H,
390    ) -> Result<(), EngineError>
391    where
392        H: AsyncHandler<State, Sig>,
393    {
394        self.exports
395            .push(Export::from_async_handler(name, handler)?);
396        Ok(())
397    }
398
399    /// Stage a tracing-backed log export with type `a -> str where Show a`.
400    pub fn export_tracing_log_function(
401        &mut self,
402        name: impl Into<String>,
403        log: fn(&str),
404    ) -> Result<(), EngineError> {
405        let name = name.into();
406        let name_sym = sym(&name);
407        let scheme = Self::tracing_log_scheme();
408        self.exports.push(Export::from_native_async(
409            name,
410            scheme,
411            1,
412            move |engine, call_type, args| {
413                let name_sym = name_sym.clone();
414                async move {
415                    if args.len() != 1 {
416                        return Err(EngineError::NativeArity {
417                            name: name_sym.clone(),
418                            expected: 1,
419                            got: args.len(),
420                        });
421                    }
422
423                    let (arg_ty, _ret_ty) = match call_type.as_ref() {
424                        TypeKind::Fun(arg, ret) => (arg.clone(), ret.clone()),
425                        _ => return Err(EngineError::NotCallable(call_type.to_string())),
426                    };
427                    let show_ty = Type::fun(arg_ty.clone(), Type::builtin(BuiltinTypeId::String));
428                    let mut gas = GasMeter::default();
429                    let show_ptr = RuntimeEvaluatorRef::new(&engine)
430                        .resolve_class_method(&sym("show"), &show_ty, &mut gas)
431                        .await?;
432                    let rendered_ptr = apply(
433                        &engine,
434                        show_ptr,
435                        args[0],
436                        Some(&show_ty),
437                        Some(&arg_ty),
438                        &mut gas,
439                    )
440                    .await?;
441                    let rendered = String::from_pointer(&engine.heap, &rendered_ptr)?;
442                    log(&rendered);
443                    engine.heap.alloc_string(rendered)
444                }
445                .boxed()
446            },
447        )?);
448        Ok(())
449    }
450
451    /// Stage the standard `debug`/`info`/`warn`/`error` tracing exports.
452    pub fn export_tracing_log_functions(&mut self) -> Result<(), EngineError> {
453        self.export_tracing_log_function("debug", |s| tracing::debug!("{s}"))?;
454        self.export_tracing_log_function("info", |s| tracing::info!("{s}"))?;
455        self.export_tracing_log_function("warn", |s| tracing::warn!("{s}"))?;
456        self.export_tracing_log_function("error", |s| tracing::error!("{s}"))?;
457        Ok(())
458    }
459
460    /// Stage a pointer-level synchronous native export with an explicit Rex type scheme.
461    ///
462    /// This lower-level API is intended for dynamic or runtime-defined integrations where the
463    /// handler needs access to the engine heap or where the Rex type cannot be inferred from an
464    /// ordinary Rust function signature alone.
465    ///
466    /// `scheme` describes the Rex-visible type, and `arity` must match the number of arguments the
467    /// handler expects.
468    ///
469    /// # Examples
470    ///
471    /// ```rust,ignore
472    /// use rexlang_engine::{EvaluatorRef, Library, Pointer};
473    /// use rexlang_typesystem::{BuiltinTypeId, Scheme, Type};
474    ///
475    /// let mut library = Library::<()>::new("acme.dynamic");
476    /// let scheme = Scheme::new(
477    ///     vec![],
478    ///     vec![],
479    ///     Type::fun(Type::builtin(BuiltinTypeId::I32), Type::builtin(BuiltinTypeId::I32)),
480    /// );
481    ///
482    /// library
483    ///     .export_native("id_ptr", scheme, 1, |_engine: EvaluatorRef<'_, ()>, _typ: &Type, args: &[Pointer]| {
484    ///         Ok(args[0].clone())
485    ///     })
486    ///     .unwrap();
487    /// ```
488    pub fn export_native<F>(
489        &mut self,
490        name: impl Into<String>,
491        scheme: rexlang_typesystem::Scheme,
492        arity: usize,
493        handler: F,
494    ) -> Result<(), EngineError>
495    where
496        F: for<'a> Fn(
497                EvaluatorRef<'a, State>,
498                &'a Type,
499                &'a [Pointer],
500            ) -> Result<Pointer, EngineError>
501            + Send
502            + Sync
503            + 'static,
504    {
505        self.exports
506            .push(Export::from_native(name, scheme, arity, handler)?);
507        Ok(())
508    }
509
510    pub fn export_native_with_gas_cost<F>(
511        &mut self,
512        name: impl Into<String>,
513        scheme: rexlang_typesystem::Scheme,
514        arity: usize,
515        gas_cost: u64,
516        handler: F,
517    ) -> Result<(), EngineError>
518    where
519        F: for<'a> Fn(
520                EvaluatorRef<'a, State>,
521                &'a Type,
522                &'a [Pointer],
523            ) -> Result<Pointer, EngineError>
524            + Send
525            + Sync
526            + 'static,
527    {
528        self.exports.push(Export::from_native_with_gas_cost(
529            name, scheme, arity, gas_cost, handler,
530        )?);
531        Ok(())
532    }
533
534    /// Stage a pointer-level asynchronous native export with an explicit Rex type scheme.
535    ///
536    /// This is the async counterpart to [`Library::export_native`]. Use it when the export needs
537    /// both direct engine access and asynchronous execution.
538    ///
539    /// # Examples
540    ///
541    /// ```rust,ignore
542    /// use futures::FutureExt;
543    /// use rexlang_engine::{EvaluatorRef, Library, Pointer};
544    /// use rexlang_typesystem::{BuiltinTypeId, Scheme, Type};
545    ///
546    /// let mut library = Library::<()>::new("acme.dynamic");
547    /// let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
548    ///
549    /// library
550    ///     .export_native_async(
551    ///         "answer_async",
552    ///         scheme,
553    ///         0,
554    ///         |engine: EvaluatorRef<'_, ()>, _typ: Type, _args: Vec<Pointer>| {
555    ///             async move { engine.heap.alloc_i32(42) }.boxed()
556    ///         },
557    ///     )
558    ///     .unwrap();
559    /// ```
560    pub fn export_native_async<F>(
561        &mut self,
562        name: impl Into<String>,
563        scheme: rexlang_typesystem::Scheme,
564        arity: usize,
565        handler: F,
566    ) -> Result<(), EngineError>
567    where
568        F: for<'a> Fn(EvaluatorRef<'a, State>, Type, Vec<Pointer>) -> NativeFuture<'a>
569            + Send
570            + Sync
571            + 'static,
572    {
573        self.exports
574            .push(Export::from_native_async(name, scheme, arity, handler)?);
575        Ok(())
576    }
577
578    pub fn export_native_async_with_gas_cost<F>(
579        &mut self,
580        name: impl Into<String>,
581        scheme: rexlang_typesystem::Scheme,
582        arity: usize,
583        gas_cost: u64,
584        handler: F,
585    ) -> Result<(), EngineError>
586    where
587        F: for<'a> Fn(EvaluatorRef<'a, State>, Type, Vec<Pointer>) -> NativeFuture<'a>
588            + Send
589            + Sync
590            + 'static,
591    {
592        self.exports.push(Export::from_native_async_with_gas_cost(
593            name, scheme, arity, gas_cost, handler,
594        )?);
595        Ok(())
596    }
597
598    pub fn export_native_async_cancellable<F>(
599        &mut self,
600        name: impl Into<String>,
601        scheme: rexlang_typesystem::Scheme,
602        arity: usize,
603        handler: F,
604    ) -> Result<(), EngineError>
605    where
606        F: for<'a> Fn(
607                EvaluatorRef<'a, State>,
608                CancellationToken,
609                Type,
610                &'a [Pointer],
611            ) -> NativeFuture<'a>
612            + Send
613            + Sync
614            + 'static,
615    {
616        self.export_native_async_cancellable_with_gas_cost(name, scheme, arity, 0, handler)
617    }
618
619    pub fn export_native_async_cancellable_with_gas_cost<F>(
620        &mut self,
621        name: impl Into<String>,
622        scheme: rexlang_typesystem::Scheme,
623        arity: usize,
624        gas_cost: u64,
625        handler: F,
626    ) -> Result<(), EngineError>
627    where
628        F: for<'a> Fn(
629                EvaluatorRef<'a, State>,
630                CancellationToken,
631                Type,
632                &'a [Pointer],
633            ) -> NativeFuture<'a>
634            + Send
635            + Sync
636            + 'static,
637    {
638        self.exports
639            .push(Export::from_native_async_cancellable_with_gas_cost(
640                name, scheme, arity, gas_cost, handler,
641            )?);
642        Ok(())
643    }
644
645    pub fn export_value<V>(&mut self, name: impl Into<String>, value: V) -> Result<(), EngineError>
646    where
647        V: IntoPointer + RexType + Clone + Send + Sync + 'static,
648    {
649        self.exports.push(Export::from_value(name, value)?);
650        Ok(())
651    }
652
653    pub fn export_value_typed(
654        &mut self,
655        name: impl Into<String>,
656        typ: Type,
657        value: Value,
658    ) -> Result<(), EngineError> {
659        self.exports
660            .push(Export::from_value_typed(name, typ, value)?);
661        Ok(())
662    }
663}
664
665fn type_decl_from_adt(adt: &AdtDecl) -> TypeDecl {
666    TypeDecl {
667        span: Span::default(),
668        is_pub: true,
669        name: adt.name.clone(),
670        params: adt.params.iter().map(|p| p.name.clone()).collect(),
671        variants: adt
672            .variants
673            .iter()
674            .map(|variant| TypeVariant {
675                name: variant.name.clone(),
676                args: variant.args.iter().map(type_expr_from_type).collect(),
677            })
678            .collect(),
679    }
680}
681
682fn type_expr_from_type(typ: &Type) -> TypeExpr {
683    match typ.as_ref() {
684        TypeKind::Var(tv) => {
685            let name = tv
686                .name
687                .clone()
688                .unwrap_or_else(|| intern(&format!("t{}", tv.id)));
689            TypeExpr::Name(Span::default(), NameRef::Unqualified(name))
690        }
691        TypeKind::Con(con) => {
692            TypeExpr::Name(Span::default(), NameRef::Unqualified(con.name.clone()))
693        }
694        TypeKind::App(fun, arg) => {
695            if let TypeKind::App(head, err) = fun.as_ref()
696                && let TypeKind::Con(con) = head.as_ref()
697                && con.builtin_id == Some(rexlang_typesystem::BuiltinTypeId::Result)
698                && con.arity == 2
699            {
700                let result =
701                    TypeExpr::Name(Span::default(), NameRef::Unqualified(con.name.clone()));
702                let ok_expr = type_expr_from_type(arg);
703                let err_expr = type_expr_from_type(err);
704                let app1 = TypeExpr::App(Span::default(), Box::new(result), Box::new(ok_expr));
705                return TypeExpr::App(Span::default(), Box::new(app1), Box::new(err_expr));
706            }
707            TypeExpr::App(
708                Span::default(),
709                Box::new(type_expr_from_type(fun)),
710                Box::new(type_expr_from_type(arg)),
711            )
712        }
713        TypeKind::Fun(arg, ret) => TypeExpr::Fun(
714            Span::default(),
715            Box::new(type_expr_from_type(arg)),
716            Box::new(type_expr_from_type(ret)),
717        ),
718        TypeKind::Tuple(elems) => TypeExpr::Tuple(
719            Span::default(),
720            elems.iter().map(type_expr_from_type).collect(),
721        ),
722        TypeKind::Record(fields) => TypeExpr::Record(
723            Span::default(),
724            fields
725                .iter()
726                .map(|(name, ty)| (name.clone(), type_expr_from_type(ty)))
727                .collect(),
728        ),
729    }
730}