A kept when or else-less chain over an Option arm is that Option, flattened one level, unless an Option of it is wanted.
This commit is contained in:
parent
b8e68a48f7
commit
b814aca187
10
TODO.org
10
TODO.org
@ -49,6 +49,12 @@ found. Rules out =if let g = x= over a plain name, which is refused toward these
|
||||
CLOSED: [2026-09-26]
|
||||
Decided (138), like Swift: one level per boundary, the literal built at T first; not
|
||||
inside a container. Rules out implicit unwrapping: a T? where a T is wanted stays refused.
|
||||
** DONE A kept when over an Option body flattens one level
|
||||
CLOSED: [2026-09-26]
|
||||
Decided (140), reversing 125a: a kept =when=, else-less =if=/=elif= or =if let= chain whose
|
||||
arm is already a =T?= is a =T?=, and =T= beside =T?= arms is =T?=; a =T??= arm stays =T??=.
|
||||
Where =T??= is wanted the arm is Some of it. Rules out telling "no branch matched" apart
|
||||
from "a branch gave None" without asking for =T??=.
|
||||
** TODO The stepper does not step inside an optional chain
|
||||
=Ast.step_expr= treats a =Chain= as a leaf (its catch-all), so nothing in a chain's
|
||||
body gets a step point of its own.
|
||||
@ -68,8 +74,8 @@ Option or a dyn holds (130); over any other type, and =_=, it is refused toward
|
||||
** DONE when as a value, and get as a checked lookup
|
||||
CLOSED: [2026-09-26]
|
||||
Every one-armed =if= (and a =cond= with no =:else=) is a =when=; kept — a =let= value, a
|
||||
call's argument, a lambda's return — it is =Option(T)=, nested over an Option body
|
||||
(Rust's =bool::then=), and body-or-nil where a dyn is wanted. A =_=-inferred return's
|
||||
call's argument, a lambda's return — it is =Option(T)=, and body-or-nil where a dyn is
|
||||
wanted. Over an Option body it was nested (Rust's =bool::then=, 125a); 140 reversed that. A =_=-inferred return's
|
||||
last form is not kept. =get= over dyn text or vec is nil when out of range; =.field= still traps.
|
||||
|
||||
** NEXT str and String
|
||||
|
||||
109
lib/check.ml
109
lib/check.ml
@ -8618,12 +8618,31 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
| Some Types.Dyn -> Some Types.Dyn
|
||||
| _ -> None
|
||||
in
|
||||
let t = branch ctx (fun () -> in_tail (fun () -> check ctx ?want:tw t)) in
|
||||
let arm w = branch ctx (fun () -> in_tail (fun () -> check ctx ?want:w t)) in
|
||||
(* At an Option want the arm is asked the payload first, and the chain
|
||||
wraps it; refused there, it is asked the Option itself, which it
|
||||
then is (decision 140). So a T?? wanted of a T? arm is Some of it,
|
||||
and the chain's own None stays the outer one. *)
|
||||
let t, at_payload =
|
||||
match want with
|
||||
| Some (Types.Option _) ->
|
||||
(match trial ctx (fun () -> arm tw) with
|
||||
| Ok t -> t, true
|
||||
| Error d ->
|
||||
(match trial ctx (fun () -> arm want) with
|
||||
| Ok t -> t, false
|
||||
| Error _ -> raise (Loc.Error d)))
|
||||
| _ -> arm tw, false
|
||||
in
|
||||
let rest ~used ?want () =
|
||||
branch ctx (fun () ->
|
||||
ctx.tail <- tail; ctx.used <- used; check ctx ?want e)
|
||||
in
|
||||
match t.Tast.ty with
|
||||
| ty when at_payload && not (Types.equal ty Types.Never) ->
|
||||
let oty = Option.get want in
|
||||
let e = rest ~used:true ~want:oty () in
|
||||
mk loc oty (Tast.If (c, mk loc oty (Tast.Some_ t), e))
|
||||
| Types.Unit ->
|
||||
expect ctx loc ~want
|
||||
(mk loc Types.Unit (Tast.If (c, t, rest ~used:false ())))
|
||||
@ -8633,32 +8652,14 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
| Types.Dyn ->
|
||||
let e = rest ~used:true ~want:Types.Dyn () in
|
||||
expect ctx loc ~want (mk loc Types.Dyn (Tast.If (c, t, e)))
|
||||
(* An arm that is already an Option is the chain's value as it is,
|
||||
and [None] when no test holds — one level flattened (decision
|
||||
140): an arm's None and no arm running are one answer. *)
|
||||
| Types.Option _ as o ->
|
||||
let e = rest ~used:true ~want:o () in
|
||||
expect ctx loc ~want (mk loc o (Tast.If (c, t, e)))
|
||||
| ty ->
|
||||
let oty = Types.Option ty in
|
||||
(* A later arm that is a T? where this one is a T: the arms meet at
|
||||
T? (decision 138), so the chain is a T??, as it is when the T?
|
||||
arm comes first. Tried only where the arm is refused at T. *)
|
||||
let nested =
|
||||
match want, ty with
|
||||
| None, (Types.Option _ | Types.Unit) -> None
|
||||
| None, _ ->
|
||||
(match trial ctx (fun () -> rest ~used:true ~want:oty ()) with
|
||||
| Ok e -> Some (Error e)
|
||||
| Error _ ->
|
||||
let ooty = Types.Option oty in
|
||||
(match trial ctx (fun () -> rest ~used:true ~want:ooty ()) with
|
||||
| Ok e -> Some (Ok e)
|
||||
| Error _ -> None))
|
||||
| _ -> None
|
||||
in
|
||||
match nested with
|
||||
| Some (Ok e) ->
|
||||
let ooty = Types.Option oty in
|
||||
let t = mk loc ooty (Tast.Some_ (mk loc oty (Tast.Some_ t))) in
|
||||
mk loc ooty (Tast.If (c, t, e))
|
||||
| Some (Error e) ->
|
||||
mk loc oty (Tast.If (c, mk loc oty (Tast.Some_ t), e))
|
||||
| None ->
|
||||
let e = rest ~used:true ~want:oty () in
|
||||
expect ctx loc ~want
|
||||
(mk loc oty (Tast.If (c, mk loc oty (Tast.Some_ t), e))))
|
||||
@ -8899,9 +8900,9 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
branch evaluates to. Kept — a [let]'s value, an argument, a return, or
|
||||
anything else with a type wanted of it — it answers (Option T): [Some] of
|
||||
the branch when the test held and [None] when it did not. A branch that
|
||||
is already an Option is not flattened: the answer is (Option (Option T)),
|
||||
Rust's [bool::then], so [None] from the branch and a failed test stay two
|
||||
answers.
|
||||
is already an Option is that Option, flattened one level (decision 140,
|
||||
Kotlin's [?.] rather than Rust's [bool::then]): [None] from the branch and
|
||||
a failed test are one answer. A (Option (Option T)) branch stays one.
|
||||
|
||||
Dyn has no Option. Where a dyn is wanted, or the branch is a dyn, a false
|
||||
test answers nil and a true one the branch's value — one absence, as a
|
||||
@ -8921,17 +8922,26 @@ and check_when ctx ~used ?want loc c
|
||||
| Some Types.Dyn ->
|
||||
let t = branch_at ~want:Types.Dyn () in
|
||||
mk loc Types.Dyn (Tast.If (c, t, nil ()))
|
||||
| Some (Types.Option inner) ->
|
||||
let t = branch_at ~want:inner () in
|
||||
let oty = Types.Option inner in
|
||||
let some = if t.Tast.ty = Types.Never then t else mk loc oty (Tast.Some_ t) in
|
||||
mk loc oty (Tast.If (c, some, mk loc oty Tast.None_))
|
||||
| Some (Types.Option inner as oty) ->
|
||||
(* The payload first, wrapped; refused there, the Option itself, which
|
||||
the branch then is (decision 140). *)
|
||||
let t =
|
||||
match trial ctx (fun () -> branch_at ~want:inner ()) with
|
||||
| Ok t -> if t.Tast.ty = Types.Never then t else mk loc oty (Tast.Some_ t)
|
||||
| Error d ->
|
||||
(match trial ctx (fun () -> branch_at ~want:oty ()) with
|
||||
| Ok t -> if t.Tast.ty = Types.Never then t else expect ctx loc ~want:(Some oty) t
|
||||
| Error _ -> raise (Loc.Error d))
|
||||
in
|
||||
mk loc oty (Tast.If (c, t, mk loc oty Tast.None_))
|
||||
| None when not used -> stmt (branch_at ())
|
||||
| _ ->
|
||||
let t = branch_at () in
|
||||
if valueless t then stmt t
|
||||
else if Types.equal t.Tast.ty Types.Dyn then
|
||||
expect ctx loc ~want (mk loc Types.Dyn (Tast.If (c, t, nil ())))
|
||||
else if (match t.Tast.ty with Types.Option _ -> true | _ -> false) then
|
||||
expect ctx loc ~want (mk loc t.Tast.ty (Tast.If (c, t, mk loc t.Tast.ty Tast.None_)))
|
||||
else
|
||||
let oty = Types.Option t.Tast.ty in
|
||||
expect ctx loc ~want
|
||||
@ -10021,7 +10031,7 @@ and check_array_gen ctx ~want loc dims f =
|
||||
~element:(fun idxs -> mk loc elem (Tast.CallPtr (fv, idxs))))
|
||||
|
||||
and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = false)
|
||||
?opt_rest ?want loc scrutinee arms =
|
||||
?(flat = false) ?opt_rest ?want loc scrutinee arms =
|
||||
(* [stmt] is an [if let] with no else: a statement, Unit whatever its arm
|
||||
answers, as a one-armed [if] is when nothing keeps it. [opt] is one that
|
||||
is kept: its arm answers [Some], and the arm with no body [None]. *)
|
||||
@ -10030,7 +10040,11 @@ and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = fals
|
||||
let want0 = want in
|
||||
let want =
|
||||
if stmt then None
|
||||
else if opt then (match want with Some (Types.Option i) -> Some i | _ -> None)
|
||||
else if opt then
|
||||
(match want with
|
||||
| Some (Types.Option _) when flat -> want
|
||||
| Some (Types.Option i) -> Some i
|
||||
| _ -> None)
|
||||
else want
|
||||
in
|
||||
let s = check ctx scrutinee in
|
||||
@ -10629,6 +10643,14 @@ and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = fals
|
||||
match r with
|
||||
| Some e -> e
|
||||
| None -> rt a.Ast.aloc Types.Dyn "flan_dyn_nil" [])
|
||||
(* Already an Option: the whole, one level flattened (decision 140). *)
|
||||
| Some (Types.Option _ as o) when flat || want0 = None ->
|
||||
opt_result := Some o;
|
||||
let r = rest ~used:true ~want:o () in
|
||||
fill Fun.id (fun a ->
|
||||
match r with
|
||||
| Some e -> e
|
||||
| None -> mk a.Ast.aloc o Tast.None_)
|
||||
| Some t ->
|
||||
let oty = Types.Option t in
|
||||
opt_result := Some oty;
|
||||
@ -10791,9 +10813,22 @@ and check_if_let ctx ~tail ~used ?want loc scrutinee (arm : Ast.arm) els =
|
||||
ctx.tail <- tail; ctx.used <- used; check ctx ?want e))
|
||||
els
|
||||
in
|
||||
(* The arm at the payload first, as [check_when] asks it; refused
|
||||
there, at the Option itself (decision 140). *)
|
||||
let go ~flat () =
|
||||
check_match ctx ~tail ~used:true ~opt:true ~flat ?opt_rest ?want loc scrutinee
|
||||
[ arm; wild [] ]
|
||||
in
|
||||
expect ctx loc ~want
|
||||
(check_match ctx ~tail ~used:true ~opt:true ?opt_rest ?want loc scrutinee
|
||||
[ arm; wild [] ]))
|
||||
(match want with
|
||||
| Some (Types.Option _) ->
|
||||
(match trial ctx (go ~flat:false) with
|
||||
| Ok r -> r
|
||||
| Error d ->
|
||||
(match trial ctx (go ~flat:true) with
|
||||
| Ok r -> r
|
||||
| Error _ -> raise (Loc.Error d)))
|
||||
| _ -> go ~flat:false ()))
|
||||
| Some e ->
|
||||
check_match ctx ~tail ~used ?want loc scrutinee [ arm; wild [ e ] ]
|
||||
| None ->
|
||||
|
||||
@ -252,7 +252,8 @@ Each item: the proposal, then the reason in one line.
|
||||
into a `T??` is `Some` of it. A `$t` meeting `$u?` binds `$u` to `T`.
|
||||
Never inside a container (`Vec(i32)` is not a `Vec(i32?)`), and never the
|
||||
other way: a `T?` where a `T` is wanted still needs `!`, `??`, `x?` or
|
||||
`as`. A kept chain whose arms are a `T` and a `T?` is a `T??`. **Built.**
|
||||
`as`. A kept chain whose arms are a `T` and a `T?` is a `T?` (decision
|
||||
140, under `when c`). **Built.**
|
||||
- **Casts and type-taking builtins are calls:** `i32(x)`, `vec-new(u8)`,
|
||||
`max-value(u8)`, `the([3 f32], [1 2 3.5])`. A pointer cast is the type
|
||||
called: `Ptr(Color)(p)` reads `((Ptr Color) p)`. **Built.**
|
||||
@ -316,7 +317,13 @@ Each item: the proposal, then the reason in one line.
|
||||
A `when` whose value is kept (a `let`'s value, an argument, a return) gives
|
||||
`Some(a)` when `c` holds and `None` when it does not; where a `dyn` is
|
||||
wanted, `a` or `nil`. As a statement it gives nothing. An `if`/`elif` chain
|
||||
with no `else` is the same when kept: `None` when no test holds. **Built.**
|
||||
with no `else` is the same when kept: `None` when no test holds. When `a`
|
||||
is already an Option it is not wrapped again (decision 140): `when c then
|
||||
o` over an `i32?` is an `i32?`, `None` when `c` fails or `o` is `None`, and
|
||||
a chain mixing `T` and `T?` arms is a `T?`. One level only: an arm that is
|
||||
a `T??` gives a `T??`. Where an Option of the arm's type is wanted, as a
|
||||
`T??` over a `T?` arm, the arm is `Some` of its value and a failed test is
|
||||
the outer `None`. `if let` with no `else` follows the same rule. **Built.**
|
||||
- **`if let P = v`** plus a block reads as `(if-let [P v] then)`; `elif` and
|
||||
`else` follow as for `if`, the rest of the chain being the `if-let`'s else.
|
||||
`elif let P = v` is a further `if-let` nested in that else.
|
||||
|
||||
@ -40,12 +40,24 @@ fn arms(k: i32, x: i32) -> i32?
|
||||
|
||||
fn big(x: i64?) -> i64 = x ?? 0
|
||||
|
||||
;; A T?? is wanted, so each arm is Some of its value and the chain's None is
|
||||
;; the outer one.
|
||||
fn chain(a: bool, b: bool, opt: i32?) -> Option(i32?)
|
||||
if a
|
||||
1
|
||||
elif b
|
||||
opt
|
||||
|
||||
;; Nothing wanted: a T arm beside a T? arm makes the chain a T?, flattened
|
||||
;; one level (decision 140).
|
||||
fn flat(a: bool, b: bool, opt: i32?) -> i32?
|
||||
let r =
|
||||
if a
|
||||
1
|
||||
elif b
|
||||
opt
|
||||
r
|
||||
|
||||
fn main()
|
||||
;; assignment, and a literal at the payload's width
|
||||
let s: i64? = None
|
||||
@ -85,14 +97,15 @@ fn main()
|
||||
let none: i32? = None
|
||||
let nn2: Option(i32?) = none
|
||||
println(two(nn), two(nn2))
|
||||
;; a kept chain: a T arm beside a T? arm makes the chain a T??
|
||||
;; kept chains over T and T? arms
|
||||
println(two(chain(true, false, none)), two(chain(false, true, none)), two(chain(false, false, none)))
|
||||
println(flat(true, false, none) ?? -1, flat(false, true, Some(6)) ?? -1, flat(false, true, none) ?? -1, flat(false, false, none) ?? -1)
|
||||
let kk =
|
||||
if x > 9
|
||||
none
|
||||
elif x > 1
|
||||
1
|
||||
println(two(kk))
|
||||
println(kk ?? -1)
|
||||
;; generics
|
||||
println(first(9), first(Some(8)), wrap(3) ?? 0, through(5))
|
||||
;; a literal local takes the payload's type from an Option use, as it
|
||||
|
||||
@ -21,6 +21,28 @@ fn early(a: Option(i32)) -> Option(i32)
|
||||
elif true
|
||||
3
|
||||
|
||||
;; An arm that is already an Option is the whole, flattened (decision 140).
|
||||
fn flat(a: Option(i32), o: Option(i32)) -> Option(i32)
|
||||
if let Some(x) = a then o
|
||||
|
||||
;; An Option of it wanted: the arm is Some of its value, and no match is the
|
||||
;; outer None.
|
||||
fn nest(a: Option(i32), o: Option(i32)) -> Option(Option(i32))
|
||||
if let Some(x) = a then o
|
||||
|
||||
fn level(oo: Option(Option(i32)))
|
||||
match oo
|
||||
Some(o) -> if o? then println(o) else println("some none")
|
||||
None -> println("none")
|
||||
|
||||
fn flat_chain(a: Option(i32), k: i32)
|
||||
let r =
|
||||
if let Some(x) = a
|
||||
x
|
||||
elif k > 0
|
||||
None
|
||||
show(r)
|
||||
|
||||
fn dyn_only(a: Option(i32)) -> dyn
|
||||
if let Some(x) = a then x
|
||||
|
||||
@ -40,6 +62,15 @@ fn main()
|
||||
show(lead(1, None))
|
||||
show(early(None))
|
||||
show(early(Some(1)))
|
||||
show(flat(Some(1), Some(4)))
|
||||
show(flat(Some(1), None))
|
||||
show(flat(None, Some(4)))
|
||||
level(nest(Some(1), Some(4)))
|
||||
level(nest(Some(1), None))
|
||||
level(nest(None, Some(4)))
|
||||
flat_chain(Some(3), 0)
|
||||
flat_chain(None, 1)
|
||||
flat_chain(None, 0)
|
||||
println(dyn_only(Some(5)))
|
||||
println(dyn_only(None))
|
||||
;; As a statement it is unchanged.
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
;;;; A when whose value is kept answers an Option: Some of its body when the
|
||||
;;;; test holds, None when it does not. As a statement it answers nothing.
|
||||
;;;; Where a dyn is wanted it answers the body or nil, since dyn has no
|
||||
;;;; Option. A body that is already an Option is not flattened.
|
||||
;;;; Option. A body that is already an Option is that Option, flattened one
|
||||
;;;; level (decision 140), unless an Option of it is what is wanted.
|
||||
|
||||
(defn show [o (Option i32)] ()
|
||||
(match o (Some v) (println v) None (println "none")))
|
||||
@ -9,10 +10,13 @@
|
||||
;; Returned: the return type is the want.
|
||||
(defn half [n i32] (Option i32) (when (= 0 (% n 2)) (/ n 2)))
|
||||
|
||||
;; Nested, as Rust's bool::then: None from the body stays apart from a
|
||||
;; failed test.
|
||||
;; An (Option (Option i32)) is wanted, so the body is Some of it and the
|
||||
;; failed test is the outer None.
|
||||
(defn wrap [c bool o (Option i32)] (Option (Option i32)) (when c o))
|
||||
|
||||
;; Flattened: None from the body and a failed test are one answer.
|
||||
(defn flat [c bool o (Option i32)] (Option i32) (when c o))
|
||||
|
||||
(defn level [oo (Option (Option i32))] ()
|
||||
(match oo
|
||||
(Some o) (match o (Some v) (println v) None (println "some none"))
|
||||
@ -45,6 +49,12 @@
|
||||
(level (wrap true (Some 1)))
|
||||
(level (wrap true None))
|
||||
(level (wrap false (Some 1)))
|
||||
(show (flat true (Some 4)))
|
||||
(show (flat true None))
|
||||
(show (flat false (Some 4)))
|
||||
(let [o (the (Option i32) (Some 8))
|
||||
f (when true o)]
|
||||
(show f))
|
||||
(println (dyn-when true))
|
||||
(println (dyn-when nil))
|
||||
(show (early None))
|
||||
|
||||
@ -2226,8 +2226,8 @@ let () =
|
||||
dyn_if_truthy_out;
|
||||
(* A kept when is an Option; get is a checked lookup; if let. *)
|
||||
let when_value_out =
|
||||
"5\nnone\n42\nnone\n9\n1\nsome none\nnone\n5\nnil\n3\nnone\n6\nnone\n20\nnone\n2\n\
|
||||
a\nnil\ncond stmt\nran\nend\n"
|
||||
"5\nnone\n42\nnone\n9\n1\nsome none\nnone\n4\nnone\nnone\n8\n5\nnil\n3\nnone\n6\nnone\n\
|
||||
20\nnone\n2\na\nnil\ncond stmt\nran\nend\n"
|
||||
in
|
||||
outputs "when as a value" "programs/when-value.flan" when_value_out;
|
||||
outputs ~opt:"-O0" "when as a value, -O0" "programs/when-value.flan" when_value_out;
|
||||
@ -2246,7 +2246,8 @@ let () =
|
||||
"5\n100\n0\n9\n1\n20\n100\n0\n1\n7\nabsent\nnorth\n6\n-1\n-2\n14\nwhen block\n"
|
||||
in
|
||||
let if_let_kept_out =
|
||||
"1\n4\nnone\n6\nnone\n9\n2\nnone\n3\nnone\n5\nnil\n7\n"
|
||||
"1\n4\nnone\n6\nnone\n9\n2\nnone\n3\nnone\n4\nnone\nnone\n4\nsome none\nnone\n\
|
||||
3\nnone\nnone\n5\nnil\n7\n"
|
||||
in
|
||||
outputs "a kept if let chain" "programs/if-let-kept.fln" if_let_kept_out;
|
||||
outputs ~opt:"-O0" "a kept if let chain, -O0" "programs/if-let-kept.fln" if_let_kept_out;
|
||||
@ -2277,7 +2278,7 @@ let () =
|
||||
(* A T where a T? is wanted is Some of it (decision 138). *)
|
||||
("autowrap.fln",
|
||||
"-1\n5 4 2\n7 8 -1\n3 4\n7\n1 0 4\n5\n2 6 0\n2 -1 -1 2\n4\n-1 3 0\n\
|
||||
some some some none\nsome some some none none\nsome some\n9 8 3 5\n\
|
||||
some some some none\nsome some some none none\n1 6 -1 -1\n1\n9 8 3 5\n\
|
||||
4000000000 4000000000 4 4\n4 4000000000\n3.5 3.5 7 7\n200 200\n-1 5\n1 0\n10\n");
|
||||
(* x? tests and narrows, e? as g names what it found (decision 133). *)
|
||||
("presence.fln", "true false true\n6\n-1\n3\n101 209 0\n11\n42\n2\nabsent\n6\nfalse true\n3\n6\n15\n"); ("presence-dyn.fln", "true false\n103 209 0\nno pet\nann\n3 2\n") ];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user