1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use tan::{
context::Context,
error::Error,
expr::{expr_clone, Expr},
util::{
args::{unpack_arg, unpack_stringable_arg},
module_util::require_module,
},
};
// #todo implement all these with Tan.
// pub fn maybe_new() -> Result<Expr, Error> {
// unimplemented!()
// }
// #todo rename to nil?/is-nil?
// #insight with dynamic typing you don't strictly need a Maybe type?
pub fn is_none(args: &[Expr]) -> Result<Expr, Error> {
let [expr] = args else {
// #todo better error
return Err(Error::invalid_arguments("one argument expected", None));
};
Ok(Expr::Bool(expr.is_none()))
}
pub fn is_some(args: &[Expr]) -> Result<Expr, Error> {
let [expr] = args else {
// #todo better error
return Err(Error::invalid_arguments("one argument expected", None));
};
Ok(Expr::Bool(!expr.is_none()))
}
// #todo #IMPORTANT this should be a special form, not evaluate the default value if not needed (short-circuit).
// #todo implement with tan!
pub fn some_or(args: &[Expr]) -> Result<Expr, Error> {
let expr = unpack_arg(args, 0, "expr")?;
if expr.is_none() {
let fallback = unpack_arg(args, 1, "fallback")?;
// #todo #optimize remove the fucking clone.
// #todo consider ForeignFunction variant that returns Result<&Expr, Error>
// #todo or maybe a macro can consume expressions?
return Ok(expr_clone(fallback));
}
// #todo the nasty clone again.
Ok(expr_clone(expr))
}
pub fn expect(args: &[Expr], context: &mut Context) -> Result<Expr, Error> {
let expr = unpack_arg(args, 0, "expr")?;
// #todo Should handle proper Maybe type.
if expr.is_none() {
let text = unpack_stringable_arg(args, 1, "text")?;
let text = format!("Failed expect: {text}");
// #todo Improve reporting of failed expect.s
// #todo Introduce throw_panic helper.
return Err(Error::panic_with_context(&text, context));
}
// #todo #fixme the nasty clone again.
Ok(expr_clone(expr))
}
pub fn setup_lib_maybe(context: &mut Context) {
//. #todo move to `maybe` namespace?
let module = require_module("prelude", context);
// #insight Use `is-some?` instead of `some?` to make it a verb, `is` is a linking verb.
// (if (is-some? user) ...)
module.insert_invocable("is-some?", Expr::foreign_func(&is_some));
module.insert_invocable("is-none?", Expr::foreign_func(&is_none));
module.insert_invocable("some-or", Expr::foreign_func(&some_or));
module.insert_invocable("expect", Expr::foreign_func_mut_context(&expect));
}
// #todo add more tan unit tests!