From bfb377f0db4c1b83f1bcf4325a127c9dfb170fe2 Mon Sep 17 00:00:00 2001
From: Per Lindgren <per.lindgren@ltu.se>
Date: Sun, 13 Sep 2020 14:53:52 +0200
Subject: [PATCH] fallible iterator, with and without errors

---
 src/ast/main.rs | 68 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 18 deletions(-)

diff --git a/src/ast/main.rs b/src/ast/main.rs
index a669a9b..08deadd 100644
--- a/src/ast/main.rs
+++ b/src/ast/main.rs
@@ -76,27 +76,45 @@ fn type_check2(stmts: &Stmts) -> Result<Id, Error> {
 // IMHO Result::map should have been named map_ok, since that is what is does, and symmetric to map_err.
 
 fn type_check3(stmts: &Stmts) -> Result<Id, Error> {
-    let res =
-        stmts
-            .iter()
-            .try_fold::<_, _, Result<Id, Error>>("".to_string(), |_, stmt| match stmt {
-                Stmt::Let(id, _) => {
-                    println!("let {} ...", id);
-                    id.to_string();
-                    Ok(id.to_string())
-                }
-                Stmt::If(_, _, __) => {
-                    println!("if has an error");
-                    Err(format!("error found in statement {:?}", stmt))
-                }
-            });
+    let res = stmts.iter().try_fold("".to_string(), |_, stmt| match stmt {
+        Stmt::Let(id, _) => {
+            println!("let {} ...", id);
+            Ok(id.to_string())
+        }
+        Stmt::If(_, _, __) => {
+            println!("if has an error");
+            Err(format!("error found in statement {:?}", stmt))
+        }
+    });
 
     println!("here we can do something before returning");
     res
 }
-// this is quite good, we can get rid of some book keeping, and we iterate only up to the point where an error occurs.
-// we have to convert the Ok(()) into the last Id (using Result::map)
-// IMHO Result::map should have been named map_ok, since that is what is does, and symmetric to map_err.
+// this is likely as good as it gets using Rust.
+// the `try_fold`, aborts on an error directly.
+// we need to give an initial value for the accumulator ("".to_string())
+// in this case we don't accumulate, so the first closure parameter is discarded (`_`)
+// instead we just overwrite the accumatore with the latest seen identifier.
+
+fn type_check4(stmts: &Stmts) -> Result<Id, Error> {
+    let res = stmts
+        .iter()
+        .try_fold("".to_string(), |_, stmt| match stmt {
+            Stmt::Let(id, _) => {
+                println!("let {} ...", id);
+                Ok(id.to_string())
+            }
+            Stmt::If(_, _, __) => {
+                println!("if has an error");
+                Err(format!("error found in statement {:?}", stmt))
+            }
+        })?;
+
+    println!("here we can do something before returning");
+    Ok(res)
+}
+// A slight alternation av type_check3, here we make an early return
+// Notice that the type of `res` has changed.
 
 #[test]
 fn test_stmts() {
@@ -108,5 +126,19 @@ fn test_stmts() {
     ];
     println!("type_check {:?}", type_check(&stmts));
     println!("type_check2 {:?}", type_check2(&stmts));
-    println!("type_check3 {:?}", type_check2(&stmts));
+    println!("type_check3 {:?}", type_check3(&stmts));
+    println!("type_check4 {:?}", type_check4(&stmts));
+}
+
+#[test]
+fn test_stmts_without_error() {
+    let stmts = vec![
+        Stmt::Let("a".to_string(), NumOrId::Num(1)),
+        Stmt::Let("b".to_string(), NumOrId::Num(2)),
+        Stmt::Let("c".to_string(), NumOrId::Num(3)),
+    ];
+    println!("type_check {:?}", type_check(&stmts));
+    println!("type_check2 {:?}", type_check2(&stmts));
+    println!("type_check3 {:?}", type_check3(&stmts));
+    println!("type_check4 {:?}", type_check4(&stmts));
 }
-- 
GitLab