Fuzion Logo
flang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

stream.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature stream
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# stream -- a stream of values
#
# NYI: Check if stream should be replaced by a lazy list, which is a choice
# of either nil or a tuple (head, tail). This should avoid the need to store
# mutable state.
stream<T> ref : Sequence<T> is

  # create a list from this stream
  #
  redef asList list<T> is
    fromStream list<T> is
      if hasNext
        h := next;
        ref : Cons<T,list<T>>
          memoizedTail option<some<list<T>>> := nil
          head => h
          tail =>
            if !memoizedTail.exists
              set memoizedTail := some fromStream
            memoizedTail.get.val
      else
        nil
    fromStream


  # create a stream of T.
  #
  # A stream contains mutable state, so it cannot be reused or shared
  # between threads.
  #
  # Default implementation uses asList.  Heirs must redefine at least
  # one of asList or asStream.
  #
  redef asStream stream<T> is stream.this


  # apply f to all elements in this stream
  #
  redef forAll(f T -> unit) unit is while hasNext do f(next)


  # apply f to all elements in this stream, infix operator synonyme of forAll.
  #
  redef infix | (f T -> unit) unit is while hasNext do f(next)


  # apply 'f' to each element 'e' as long as 'f e'
  #
  redef forWhile(f T -> bool) unit is while hasNext && f(next)


  # create a new stream that contains the first elements of this stream for
  # which 'f e' is false
  #
  redef before(f T -> bool) stream<T> is before0 f
  before0(f T -> bool) ref : stream<T> is
    nextCache := stream.this.next  # NYI should be option<T> and set to None if !stream.this.hasNext
    redef hasNext => !f(nextCache)
    redef next T is
      res := nextCache
      set nextCache := stream.this.next
      res


  # create new stream from all elements for which predicate f is true
  #
  redef filter(f T -> bool) stream<T> is filter0 f
  filter0(f T -> bool) ref : stream<T> is
    cache T := ?  # NYI: should be option<T>
    cacheOk := false
    next2
    redef hasNext => cacheOk
    redef next T is
      res := cache
      set cacheOk := false
      next2
      res
    next2 unit is
      while !cacheOk && stream.this.hasNext
        set cache := stream.this.next
        set cacheOk := f(cache)


  # create a new stream from all elements for which predicate f is true, infix
  # operator synonyme of filter.
  #
  redef infix |& (f T -> bool) stream<T> is filter(f)


  # create a new stream from all elements for which predicate f is true, infix
  # operator synonyme of filter.
  #
  # NYI: What is better, 'infix |&' or 'infix &', or something else?
  #
  redef infix & (f T -> bool) => filter f


  # check if predicate f holds for all elements produced by this stream
  #
  redef infix ∀ (f T -> bool) bool is
    while hasNext: f next   # hasNext implies f next
    until !hasNext


  # check if predicate f holds for at least one element produced by this stream
  #
  redef infix ∃ (f T -> bool) bool is
    while hasNext
    until f next


  # does this stream have one more element?
  #
  hasNext bool is abstract


  # the next element in this stream
  #
  next T
  /* NYI: C backend creates broken code for calling precondition of abstract feature
    pre
      hasNext
   */
  is abstract


  # get the next element or nil if !hasNext
  #
  nextIfExists option<T> is
    if hasNext
      next
    else
      nil


  # print the elements of this stream
  #
  print unit is
    forAll (x ->
      yak x
      if hasNext then yak ", ")


  # count the elements of this stream
  #
  redef count i32 is
    # NYI: check if this works: map<i32>(x -> 1).fold i32.sum
    res := 0
    forAll (x -> set res := res + 1)
    res


  # collect all items from this stream into an array
  #
  redef asArray array<T> is
    for
      a := (marray 0 (sys.array<T> 0) unit), a.add x
    while hasNext
      x := next
    else
      a.as_array


  # create a stream that consists of all be the elements if this stream followed
  # by all the elements of s
  #
  concatStreams (s stream<T>) ref : stream<T> is
    hasNext => stream.this.hasNext || s.hasNext
    next => if (stream.this.hasNext) stream.this.next else s.next


  # create a string from the elements of this stream
  #
  redef asString ref string is
    # NYI: check if this works: map<string>(x-> x.asString).fold strings.concat ", "
    #
    stream.this.asString ", "


  # create a string representation of this list including all the string
  # representations of its contents, separated by 'sep'.
  #
  redef asString (sep string) => asList.asString sep


  # map the stream to a new stream applying function f to all elements
  #
  # This performs a lazy mapping, f is called only when the elements
  # are taken from the stream.
  #
  map<B>(f T -> B) stream<B> is
    ref : stream<B>
      hasNext => stream.this.hasNext
      next B is f stream.this.next


  # NYI: This currently does not work with the C backend since generics resolution
  # for stream.this.next confuses the type from the outer feature (T) with the type
  # from the inherited feature (B).
  map_broken <B>(f T -> B) : stream<B> is
    hasNext => stream.this.hasNext
    next B is f stream.this.next


  # fold the elements of this stream using the given monoid.
  #
  # e.g., to sum the elements of a stream of i32, use s.fold i32.sum
  #
  redef fold (m Monoid<T>) => fold m.e, m


  # fold the elements of this stream using the given monoid m and initial value s.
  #
  # e.g., to sum the elements of a stream of i32, use s.fold i32.sum
  #
  fold (s T, m Monoid<T>) =>
    for
      r := s, m.op r, next
    while hasNext
    else
      r