Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion devel/210_5.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 210_5

## 2026/4/13 实现 either-flat-map

## 2026/1/20 修改either的实现方法

## 2026/1/19 开源either相关的代码
## 2026/1/19 开源either相关的代码
14 changes: 13 additions & 1 deletion goldfish/liii/either.scm
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from-right to-right
either? ; 导出通用判断函数
either-left? either-right?
either-map either-for-each
either-map either-flat-map either-for-each
either-get-or-else
either-or-else
either-filter-or-else
Expand Down Expand Up @@ -122,6 +122,18 @@
) ;if
) ;define

;; 扁平映射函数:如果 either 是右值,则应用返回 Either 的函数 f
(define (either-flat-map f either)
(check-either either "either-flat-map")
(unless (procedure? f)
(type-error (format #f "In function either-flat-map: argument *f* must be *procedure*! **Got ~a**" f))
) ;unless
(if (either-right? either)
(f (to-right either))
either
) ;if
Comment on lines +131 to +137
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Return value of f not validated

either-flat-map is a monadic bind — f is expected to return an Either. If a caller passes a plain-value function (e.g. (lambda (x) (* x 2))), the result is silently a non-Either, breaking the type invariant for every downstream either-* call. The error then surfaces with a confusing message in the downstream function rather than here.

Contrast with either-map, which wraps f's result in from-right unconditionally. For flat-map the wrapping is intentionally skipped, so an explicit runtime check is needed instead:

(if (either-right? either)
    (let ((result (f (to-right either))))
      (check-either result "either-flat-map: return value of f must be an Either")
      result)
    either
) ;if

) ;define

;; 遍历函数:如果 either 是右值,则应用函数 f (执行副作用)
(define (either-for-each f either)
(check-either either "either-for-each")
Expand Down
1 change: 1 addition & 0 deletions tests/liii/either-test.scm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
;; 三、高阶操作
;; 用于映射、遍历和过滤 Either 的函数
;; either-map - 仅对 Right 中的值进行映射
;; either-flat-map - 对 Right 执行返回 Either 的链式操作
;; either-for-each - 仅对 Right 中的值执行副作用
;; either-filter-or-else - 不满足条件时回退到 Left
;; either-contains? - 判断 Right 中的值是否满足目标
Expand Down
65 changes: 65 additions & 0 deletions tests/liii/either/either-flat-map-test.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
(import (liii check)
(liii error)
(liii either)
) ;import

(check-set-mode! 'report-failed)

;; either-flat-map
;; 对 Right 值执行返回 Either 的过程,并避免产生嵌套 Either。
;;
;; 语法
;; ----
;; (either-flat-map proc either)
;;
;; 参数
;; ----
;; proc : procedure?
;; 要应用到 Right 值上的函数,返回值应为 Either。
;;
;; either : either
;; 输入 Either 值。
;;
;; 返回值
;; ----
;; either
;; 若输入为 Right,则返回 proc 的结果;若输入为 Left,则原样返回。
;;
;; 注意
;; ----
;; 对 Left 具有短路特性,不会执行 proc。
;;
;; 示例
;; ----
;; (to-right (either-flat-map (lambda (x) (from-right (* x 2))) (from-right 5))) => 10
;;
;; 错误处理
;; ----
;; type-error 当 proc 不是过程或 either 不是 Either 时

(let ((left-val (from-left "error"))
(right-val (from-right 5)))
(check (to-left (either-flat-map (lambda (x) (from-right (* x 2))) left-val)) => "error")
(let ((result (either-flat-map (lambda (x) (from-right (* x 2))) right-val)))
(check-true (either-right? result))
(check (to-right result) => 10)
) ;let
) ;let

(let* ((val1 (from-right 10))
(val2 (either-flat-map (lambda (x) (from-right (+ x 5))) val1))
(val3 (either-flat-map (lambda (x) (from-right (* x 2))) val2)))
(check-true (either-right? val3))
(check (to-right val3) => 30)
) ;let*
Comment on lines +49 to +54
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing short-circuit chaining test

The existing chaining test only covers Right → Right → Right. The key monadic property is that once a step returns Left, all subsequent flat-map calls must pass it through untouched. Adding a test like:

;; Right -> Left -> (short-circuit) -> Left
(let* ((val1 (from-right 7))
       (val2 (either-flat-map (lambda (x) (from-left "err")) val1))
       (val3 (either-flat-map (lambda (x) (from-right (* x 2))) val2)))
  (check-true (either-left? val3))
  (check (to-left val3) => "err")
) ;let*

would explicitly verify the short-circuit behaviour that is the defining feature of monadic chaining.


(let ((result (either-flat-map (lambda (x) (from-left (string-append "bad: " (number->string x))))
(from-right 7))))
(check-true (either-left? result))
(check (to-left result) => "bad: 7")
) ;let

(check-catch 'type-error (either-flat-map (lambda (x) (from-right x)) "not-either"))
(check-catch 'type-error (either-flat-map "not-a-proc" (from-right 10)))

(check-report)
Loading