Skip to content
← Back

src/semantic/types/maybe.ghul

1
namespace Semantic.Types is
2
3
use Source.LOCATION
4
5
// `Ghul.MAYBE[T]` — the runtime's unconstrained-T optional
6
// carrier. is_maybe drives the implicit conversion to `T?` at
7
// slot boundaries; surfacing it as a distinct GENERIC subclass
8
// means recognition is a virtual-property check (no per-call
9
// symbol lookup, and source-side MAYBE structs inside
10
// `ghul-runtime`'s own compile stay as plain GENERIC where the
11
// conversion is correctly inactive).
12
class MAYBE: GENERIC is
13
is_maybe: bool => true
14
15
is_optional: bool => true
16
17
optional_inner_type: Type? => arguments[0]
18
19
short_description: string => get_short_description(self)
20
21
get_short_description(reference: GENERIC) -> string static => "{reference.arguments[0].short_description}?"
22
23
init(
24
location: LOCATION,
25
symbol: Symbols.Classy,
26
arguments: Collections.List[Type]
27
) is
28
super.init(location, symbol, arguments)
29
si
30
31
create(
32
location: LOCATION,
33
symbol: Symbols.Classy,
34
arguments: Collections.List[Type]
35
) -> GENERIC =>
36
Types.MAYBE(location, symbol, arguments)
37
38
// MAYBE[T] accepts a value of T via the implicit T -> T? widening,
39
// the same rule NULLABLE uses: the boxer emits a MAYBE<T>::.ctor
40
// wrap at the slot boundary. Reported as ASSIGNABLE, not SAME, so
41
// an overload resolver with both f(T) and f(T?) candidates and a T
42
// argument still prefers f(T). Shares NULLABLE's helper — both are
43
// value-type optional carriers with a single-argument present-value
44
// constructor.
45
compare(other: Type) -> Types.MATCH is
46
let direct = Types.NULLABLE.compare_optional(self, other, super.compare(other))
47
48
if cast int(direct) <= cast int(Types.MATCH.ASSIGNABLE) then
49
return direct
50
fi
51
52
// Any other `T?` lowering flowing into a MAYBE[T] slot: a
53
// reference type carrying the optional flag, or a
54
// NULLABLE[T]. The three lowerings are intercompatible by
55
// design, so the slot accepts all of them and VALUE_BOXER
56
// emits the coercion. ASSIGNABLE rather than SAME, so an
57
// exact-typed candidate still wins overload resolution.
58
if arguments.count == 1 /\ !other.is_null /\ !other.is_error /\ other.is_optional then
59
let other_inner = other.optional_inner_type
60
61
if other_inner? /\ arguments[0].is_assignable_from(other_inner) then
62
return Types.MATCH.ASSIGNABLE
63
fi
64
fi
65
66
return direct
67
si
68
69
// A MAYBE[T] formal binds T against whatever the actual's own
70
// optional carrier holds — so `f[T](value: T?)` infers T from a
71
// `string?`, an `int?`, a MAYBE[T'] or a bare non-optional
72
// actual alike. Without this the base walks the actual's
73
// ancestors looking for a MAYBE instantiation and finds none.
74
bind_type_variables(other: Type, results: Types.GENERIC_ARGUMENT_BIND_RESULTS) -> bool is
75
// `MAYBE[T]` written out by name is a plain GENERIC over the
76
// same symbol: the same carrier, so it pairs argument by argument.
77
if other.is_null \/ other.is_error \/ other.is_maybe \/ arguments.count != 1 \/ is_construction_of_same_symbol_as(other) then
78
return super.bind_type_variables(other, results)
79
fi
80
81
let other_inner = other.optional_inner_type
82
83
if other_inner? then
84
return arguments[0].bind_type_variables(other_inner, results)
85
fi
86
87
return arguments[0].bind_type_variables(other, results)
88
si
89
90
to_string() -> string => "{arguments[0]}?"
91
si
92
si