All Downloads are FREE. Search and download functionalities are using the official Maven repository.

calico.frp.frp.scala Maven / Gradle / Ivy

There is a newer version: 0.2.2
Show newest version
/*
 * Copyright 2022 Arman Bilge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (c) 2013 Functional Streams for Scala
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package calico
package frp

import cats.Monad
import cats.StackSafeMonad
import cats.data.OptionT
import cats.effect.kernel.Concurrent
import cats.effect.kernel.Resource
import cats.syntax.all.*
import fs2.Pull
import fs2.Stream
import fs2.concurrent.Signal

given [F[_]: Concurrent]: Monad[Signal[F, _]] = new StackSafeMonad[Signal[F, _]]:
  def pure[A](a: A) = Signal.constant(a)

  override def map[A, B](siga: Signal[F, A])(f: A => B) =
    Signal.mapped(siga)(f)

  def flatMap[A, B](siga: Signal[F, A])(f: A => Signal[F, B]) = new:
    def get = siga.get.flatMap(f(_).get)
    def continuous = Stream.repeatEval(get)
    def discrete = siga.discrete.switchMap(f(_).discrete)
    override def getAndDiscreteUpdates(using Concurrent[F]) =
      getAndDiscreteUpdatesImpl
    private def getAndDiscreteUpdatesImpl =
      siga.getAndDiscreteUpdates.flatMap { (a, as) =>
        f(a).getAndDiscreteUpdates.map { (b, bs) =>
          (b, (Stream.emit(bs) ++ as.map(f(_).discrete)).switchMap(identity(_)))
        }
      }

  override def ap[A, B](ff: Signal[F, A => B])(fa: Signal[F, A]) =
    new:
      def discrete: Stream[F, B] =
        nondeterministicZip(ff.discrete, fa.discrete).map(_(_))
      def continuous: Stream[F, B] = Stream.repeatEval(get)
      def get: F[B] = ff.get.ap(fa.get)

      override def getAndDiscreteUpdates(using Concurrent[F]): Resource[F, (B, Stream[F, B])] =
        getAndDiscreteUpdatesImpl

      private def getAndDiscreteUpdatesImpl =
        (ff.getAndDiscreteUpdates, fa.getAndDiscreteUpdates).mapN {
          case ((f, fs), (a, as)) =>
            (f(a), nondeterministicZip(fs, as).map { case (f, a) => f(a) })
        }

      private def nondeterministicZip[A0, A1](
          xs: Stream[F, A0],
          ys: Stream[F, A1]
      ): Stream[F, (A0, A1)] =
        type PullOutput = (A0, A1, Stream[F, A0], Stream[F, A1])

        val firstPull: OptionT[Pull[F, PullOutput, *], Unit] = for
          firstXAndRestOfXs <- OptionT(xs.pull.uncons1.covaryOutput[PullOutput])
          (x, restOfXs) = firstXAndRestOfXs
          firstYAndRestOfYs <- OptionT(ys.pull.uncons1.covaryOutput[PullOutput])
          (y, restOfYs) = firstYAndRestOfYs
          _ <- OptionT.liftF {
            Pull.output1[F, PullOutput]((x, y, restOfXs, restOfYs)): Pull[F, PullOutput, Unit]
          }
        yield ()

        firstPull.value.void.stream.flatMap { (x, y, restOfXs, restOfYs) =>
          restOfXs.either(restOfYs).scan((x, y)) {
            case ((_, rightElem), Left(newElem)) => (newElem, rightElem)
            case ((leftElem, _), Right(newElem)) => (leftElem, newElem)
          }
        }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy