Conjunction and Disjunction of Streams

Hi, Racket Discourse.

I am reimplementing some logic for my iterations over multiset-relations in terms of streams for to see what there is to see.

The two basic operations to which I need access, are the conjunction (product) and disjunction (interleaving) of streams. The latter is simple enough, being an oft shown example, but I am wondering if I am overthinking the product.

Also, is it okay to slap the stream-lazy expressions in there as I do and call it a day?

My previous implementation uses generators, so I had very little concern for actually "sequencing" things beyond yield-ing at the appropriate time.

#lang racket

(require
  "shortnames.rkt")

(provide
  stream-disj
  stream-conj)

(define (interleave x y)
  (stream-lazy
   (if (stream-empty? x)
       y
       (stream-cons
        (stream-first x) (interleave y (stream-rest x))))))

(define (stream-disj streams)
  (foldl interleave empty-stream (reverse streams)))

(define-unique stnl)
; just my own nonsense producing:
; stnl  : (gensym)
; stnl? : == stnl

(define (cycle-stream stm)
  (stream-lazy
   (stream-cons stnl (stream-append stm (cycle-stream stm)))))

(define cycle-front-to-back
  (match-lambda
    [(list inf ... fin) `(,@(map cycle-stream inf) ,fin)]))

(define (stream-conj streams #:combine [combine values])
  (if (null? streams)
      empty-stream
      (let loop ([prev streams]
                 [stms (cycle-front-to-back streams)])
        (stream-lazy
         (for/fold ([tick #true]
                    [prod (list)]
                    [stms (list)]
                    #:result
                    (cond
                      [(not prod) empty-stream]
                      [else
                       (define curr (reverse prod))
                       (stream-cons
                        (combine curr) (loop curr (reverse stms)))]))
                   ([stm (in-list stms)]
                    [old (in-list prev)]
                    #:break (not prod))
           (cond
             [(not tick)
              (values tick (cons old prod) (cons stm stms))]

             [(stream-empty? stm)
              (values tick #false stms)]
                    
             [(stnl? (stream-first stm))
              (define stm′ (stream-rest  stm))
              (define new  (stream-first stm′))
              (values
               tick (cons new prod) (cons (stream-rest stm′) stms))]

             [else
              (define new (stream-first stm))
              (values
               (not tick) (cons new prod) (cons (stream-rest stm) stms))]))))))

(stream->list
 (stream-disj
  (list (stream 1 2 3)
        (stream 4 5)
        (stream 6 7 8))))

(stream->list
 (stream-conj
  (list (stream 1 2 3)
        (stream 4 5)
        (stream 6 7 8))))

;=>
#|
'(1 4 2 6 3 5 7 8)
'((1 4 6)
  (2 4 6)
  (3 4 6)
  (1 5 6)
  (2 5 6)
  (3 5 6)
  (1 4 7)
  (2 4 7)
  (3 4 7)
  (1 5 7)
  (2 5 7)
  (3 5 7)
  (1 4 8)
  (2 4 8)
  (3 4 8)
  (1 5 8)
  (2 5 8)
  (3 5 8))
|#

Edit: like the stream-lazy I added now, is that strictly necessary? I would think so, but am unsure of the "extent" to which it applies.


Edit: best of three :crossed_fingers:


Edit: I didn't know you could match on streams! Go figure.

; much easier to read at a glance
(match stm
  [_ #:when (not tick)
   (values tick (cons old prod) (cons stm stms))]

  [(stream)
   (values tick #false stms)]

  [(stream* (? tock?) new rest) ; I renamed `stnl` to `tock` because it's cute
   (values tick (cons new prod) (cons rest stms))]

  [(stream* new rest)
   (values (not tick) (cons new prod) (cons rest stms))])

; than this, I would hazard
(cond
  [(not tick)
   (values tick (cons old prod) (cons stm stms))]
             
  [(stream-empty? stm)
   (values tick #false stms)]
             
  [(tock? (stream-first stm))
   (define stm′ (stream-rest  stm))
   (define new  (stream-first stm′))
   (values tick (cons new prod) (cons (stream-rest stm′) stms))]
             
  [else
   (define new (stream-first stm))
   (values (not tick) (cons new prod) (cons (stream-rest stm) stms))])