のゼロ元オブジェクトを作ってみた

(use gauche.collection)
(use gauche.mop.singleton)

(define-class <zero-collection-meta> (<singleton-meta>)
  ())

(define-class <zero-collection> (<collection>)
  ()
  :metaclass <zero-collection-meta>)

(define-method call-with-iterator ((class <zero-collection>) proc :key start)
  (proc (^ () #t)
        (^ () (error "zero-collection has no element"))))

(define-method call-with-builder ((class <zero-collection-meta>) proc :key size)
  (unless (or (undefined? size)
              (zero? size))
    (error "wrong size" size))
  (proc (^_ (error "cannot add element to zero-collection"))
        (cut instance-of <zero-collection>)))

これがあれば,結果の要素の型が引数の型になるジェネリックな append が書けるんじゃないのか?書いてみた.

(define-method empty? ((coll <collection>))
  (= 0 (size-of coll)))

(define-method first ((coll <collection>))
  (with-iterator (coll end? next)
    (if (end?)
        (error "no element" coll)
        (next))))
(define-method concat-to ((class <class>) (coll <collection>))
  (with-builder (class add! get)
    (for-each (cut for-each add! <>) coll)
    (get)))

(define-method concat ((coll <collection>))
  (if-let1 it (find ($ not $ (cut is-a? <> <zero-collection>) $)
                    coll)
    (concat-to (class-of it) coll)
    (instance-of <zero-collection>)))

(define-method ++-to ((class <class>) :rest args)
  (concat-to class args))

(define-method ++ (:rest args)
  (concat args))

例:

(++)                  ; => #<<zero-collection> 0xe711d8>
(++ "foo" (++) "bar") ; => "foobar"
(++ (++) "foo")       ; => "foo"

これってモノイドなのかな.<collection> として定義してあるけど,もっと一般的に定義できるかもしれない.より一般的に定義したとして,おいしくなるかどうかは分からないけれど.