aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-09-13 17:55:41 -0300
committerMarcos Magueta <maguetamarcos@gmail.com>2026-09-13 17:55:41 -0300
commit0de0e710e43ab7f84cae4b2d757918d3c28f3470 (patch)
treecec7d2bd3bdb3e2c2b89b064f32cf183d622a7cf /test/unit
parent49af3fc7c2e3b27f5925c153ce6ce9bd05f19c45 (diff)
downloadRNT-0de0e710e43ab7f84cae4b2d757918d3c28f3470.tar.gz
RNT-0de0e710e43ab7f84cae4b2d757918d3c28f3470.zip
lambda latin
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/evaluators/Lambda.ml142
1 files changed, 142 insertions, 0 deletions
diff --git a/test/unit/evaluators/Lambda.ml b/test/unit/evaluators/Lambda.ml
new file mode 100644
index 0000000..c1098cf
--- /dev/null
+++ b/test/unit/evaluators/Lambda.ml
@@ -0,0 +1,142 @@
+open Rnt
+
+module Make (S : Abstract.Storage.STORAGE) (C : Helpers.Storage.CONFIGURATOR) = struct
+ open Alcotest
+
+ module H = Helpers.Storage.Make (S) (C)
+ module SR = Rnt.Kernel.SubstantialRelation.Make (S)
+
+ let hash s =
+ Concepts.Blob.blob_of_bytes (Bytes.of_string s) |> Concepts.Hash.hash_of_blob
+
+ let datum =
+ { Concepts.Tuple.type_ = "noun";
+ attributes =
+ BatMap.String.empty
+ |> BatMap.String.add "lemma" (Concepts.Value.String "datum") }
+
+ let reticulare =
+ { Concepts.Tuple.type_ = "verb";
+ attributes =
+ BatMap.String.empty
+ |> BatMap.String.add "lemma" (Concepts.Value.String "rēticulāre") }
+
+ let not_a_program () =
+ Concepts.Condition.condition "not-a-program"
+ "A handle was expected to carry the program protocol and did not" Concepts.Condition.empty
+
+ (* Sets up the relations "noun" and "verb", each holding one Latin
+ lemma, hands [term] to the lambda evaluator, and drains the cursor
+ it returns. Change [term] in the cases below to play with the
+ calculus *)
+ let evaluate conn term =
+ let open Utilities.Result in
+ let* tx = S.start conn in
+ let singleton tuple =
+ let* relation = SR.empty tx ~schematics:(hash "schema") () in
+ SR.assert_tuple tx relation (Concepts.Tuple.Representation.to_blob tuple)
+ in
+ let* noun = singleton datum in
+ let* verb = singleton reticulare in
+ let* () = S.commit tx in
+ let* noun = SR.wrap conn noun in
+ let* verb = SR.wrap conn verb in
+ let root =
+ Kernel.Prototype.mixture
+ [ Kernel.Prototype.Directory.of_properties
+ ["noun", noun; "verb", verb; "lambda", Rnt.Evaluators.Lambda.make ()] ]
+ in
+ let context = Runtime.Context.over ~snapshot:(hash "snapshot") ~root () in
+ let* found = Runtime.Context.resolve context "lambda" in
+ let* evaluator = Option.to_result ~none:(not_a_program ()) found in
+ let* interpreter =
+ Rnt.Evaluators.Lambda.Program.from evaluator
+ |> Option.to_result ~none:(not_a_program ())
+ in
+ let* cursor = Rnt.Evaluators.Lambda.Program.invoke interpreter ~program:term context in
+ let* scan = Protocols.Cursor.require cursor in
+ let* tuples = Protocols.Cursor.drain scan () in
+ Protocols.Handle.release cursor;
+ Ok (BatFingerTree.to_list tuples)
+
+ let is lemma tuple =
+ Concepts.Hash.hash_equals (Concepts.Tuple.hash tuple) (Concepts.Tuple.hash lemma)
+
+ (* A bare name resolves through the context, so the relations under
+ the snapshot are the outer environment *)
+ let a_name_is_a_relation conn =
+ let scanned =
+ evaluate conn (Rnt.Evaluators.Lambda.Name "noun") |> Helpers.condition_as_failure
+ in
+ check int "one tuple scanned" 1 (List.length scanned);
+ check bool "the tuple that was asserted" true (List.for_all (is datum) scanned)
+
+ (* (\x. x) noun *)
+ let identity_applied_to_a_relation conn =
+ let open Rnt.Evaluators.Lambda in
+ let scanned =
+ evaluate conn (Apply (Abstract ("x", Name "x"), Name "noun"))
+ |> Helpers.condition_as_failure
+ in
+ check int "one tuple scanned" 1 (List.length scanned);
+ check bool "the tuple that was asserted" true (List.for_all (is datum) scanned)
+
+ (* (\x. \y. x) noun (\z. z), so the argument that wins is the one
+ the closure kept *)
+ let a_closure_keeps_its_binding conn =
+ let open Rnt.Evaluators.Lambda in
+ let scanned =
+ evaluate conn
+ (Apply
+ ( Apply (Abstract ("x", Abstract ("y", Name "x")), Name "noun"),
+ Abstract ("z", Name "z") ))
+ |> Helpers.condition_as_failure
+ in
+ check int "one tuple scanned" 1 (List.length scanned);
+ check bool "the tuple that was asserted" true (List.for_all (is datum) scanned)
+
+ (* The classic conditional, with no conditional in the calculus. A
+ boolean is the choice it makes, so [if] is application and both
+ branches are relations *)
+ let church_conditional conn =
+ let open Rnt.Evaluators.Lambda in
+ let true_ = Abstract ("then", Abstract ("else", Name "then")) in
+ let false_ = Abstract ("then", Abstract ("else", Name "else")) in
+ let if_ condition then_ else_ = Apply (Apply (condition, then_), else_) in
+ let declining = if_ true_ (Name "noun") (Name "verb") in
+ let conjugating = if_ false_ (Name "noun") (Name "verb") in
+ let declined = evaluate conn declining |> Helpers.condition_as_failure in
+ let conjugated = evaluate conn conjugating |> Helpers.condition_as_failure in
+ check int "one tuple on the then branch" 1 (List.length declined);
+ check bool "the then branch is the noun" true (List.for_all (is datum) declined);
+ check int "one tuple on the else branch" 1 (List.length conjugated);
+ check bool "the else branch is the verb" true (List.for_all (is reticulare) conjugated)
+
+ (* A program reducing to an abstraction names nothing to
+ enumerate *)
+ let an_abstraction_is_not_a_result conn =
+ let open Rnt.Evaluators.Lambda in
+ match evaluate conn (Abstract ("x", Name "x")) with
+ | Error condition ->
+ check bool "unapplied abstraction" true
+ (String.starts_with ~prefix:"unapplied-abstraction"
+ (Concepts.Condition.to_string_hum condition))
+ | Ok _ -> fail "an abstraction was enumerated"
+
+ let suite prefix =
+ ( "lambda/" ^ prefix,
+ [ test_case "a-name-is-a-relation" `Quick
+ (H.with_connection a_name_is_a_relation "lambda-name");
+ test_case "identity-applied-to-a-relation" `Quick
+ (H.with_connection identity_applied_to_a_relation "lambda-identity");
+ test_case "a-closure-keeps-its-binding" `Quick
+ (H.with_connection a_closure_keeps_its_binding "lambda-closure");
+ test_case "church-conditional" `Quick
+ (H.with_connection church_conditional "lambda-conditional");
+ test_case "an-abstraction-is-not-a-result" `Quick
+ (H.with_connection an_abstraction_is_not_a_result "lambda-abstraction") ] )
+end
+
+module LMDB = Make (Rnt.Backend.Storage.LMDB) (Helpers.Storage.LMDB_Configurator)
+
+let suites () = [LMDB.suite "lmdb"]