;; -*- Mode: Irken -*-

(define (alist-class)

  (define (add self k v)
    ;; should we check for it first?
    (set! self.alist (alist:entry k v self.alist)))

  (define (lookup self k0)
    (let loop ((l self.alist))
      (match l with
	(alist:nil) -> (maybe:no)
	(alist:entry k1 v1 tl) -> (if (eq? k0 k1)
				      (maybe:yes v1)
				      (loop tl)))))

  (define (lookup* self k default)
    (match (lookup self k) with
      (maybe:no) -> default
      (maybe:yes v) -> v))

  (define (get-error self k errstring)
    (match (lookup self k) with
      (maybe:no) -> (error1 errstring k)
      (maybe:yes v) -> v))

  (define (iterate self p)
    (let loop ((l self.alist))
      (match l with
	(alist:nil) -> #u
	(alist:entry k v tl) -> (begin (p k v) (loop tl)))))

  (define (map self p)
    (let loop ((acc '())
	       (l self.alist))
      (match l with
	(alist:nil) -> (reverse acc)
	(alist:entry k v tl) -> (loop (list:cons (p k v) acc) tl))))

  (define (keys self)
    (let loop ((acc '())
	       (l self.alist))
      (match l with
	(alist:nil) -> (reverse acc)
	(alist:entry k _ tl) -> (loop (list:cons k acc) tl))))

  (define (values self)
    (let loop ((acc '())
	       (l self.alist))
      (match l with
	(alist:nil) -> (reverse acc)
	(alist:entry _ v tl) -> (loop (list:cons v acc) tl))))

  (let ((methods
	 {add=add
	  get=lookup
	  get-default=lookup*
	  get-err=get-error
	  iterate=iterate
	  map=map
	  keys=keys
	  values=values}))
    ;; new method
    (lambda () {o=methods alist=(alist:nil)})))

(define alist-maker (alist-class))