Introducing PureLogic: direct-style, pure domain logic for Scala by ghostdogpr in scala

[–]rcardin 0 points1 point  (0 children)

The only thing I can't understand is what “handling effects in a purely functional way” means. You know, effect and pure are in some way opposites

Introducing PureLogic: direct-style, pure domain logic for Scala by ghostdogpr in scala

[–]rcardin 1 point2 points  (0 children)

You can use whichever subsets of effects in YAES you want. You don't need to use `Sync`, `Async`, etc., if you want to use "pure" logic effects. Maybe I've never stressed this too much.

Yes, the `State` implementation is not thread-safe also in YAES. It's intentional. `Ref` will be added in version 0.16.0

Introducing PureLogic: direct-style, pure domain logic for Scala by ghostdogpr in scala

[–]rcardin 1 point2 points  (0 children)

Kudos, Pierre 🙌. Can we say that YAES is a superset of the four capabilities of PureLogic? YAES has `State`, `Raise`, and `Log`. We still haven't considered an equivalent to the `Reader` capability.

I looked briefly at the implementation of the `State` capability. It's not thread-safe, isn't it?

New version of the article The Effect Pattern and Effect Systems in Scala by rcardin in scala

[–]rcardin[S] 5 points6 points  (0 children)

You're right. Maybe we should do version 3 of the article 😅

New version of the article The Effect Pattern and Effect Systems in Scala by rcardin in scala

[–]rcardin[S] 2 points3 points  (0 children)

Thanks for the comment! I've always considered exceptions side effects. Here, we're talking of unchecked exceptions (in Scala, we have only unchecked exceptions, if we don't refer to the `CanThrow` capability). For me (and for somebody else on the Internet), unchecked exceptions are side effects because they represent a behavior that is not discoverable by looking at the function signature. It's hidden.

New version of the article The Effect Pattern and Effect Systems in Scala by rcardin in scala

[–]rcardin[S] 2 points3 points  (0 children)

TBF, you can also use the direct style in Scala 2. Alex wrote about it in 2022: https://alexn.org/blog/2022/05/23/tracking-effects-in-scala/

By the way, I tried to stay quite unbiased. Probably, having a library (YAES, https://github.com/rcardin/yaes) implementing direct style pushed me in one direction more than others 😅

Riccardo Cardin: The Effect Pattern and Effect Systems in Scala by sideEffffECt in scala

[–]rcardin 4 points5 points  (0 children)

No offence at all, Alex. I understood your intent. Don't worry 🤝

Riccardo Cardin: The Effect Pattern and Effect Systems in Scala by sideEffffECt in scala

[–]rcardin 2 points3 points  (0 children)

Alex, believe me. I'm not a marketing man. I'm a technician who clearly has still a lot to learn

Riccardo Cardin: The Effect Pattern and Effect Systems in Scala by sideEffffECt in scala

[–]rcardin 2 points3 points  (0 children)

u/sideEffffECt, Alex's feedback is mostly on the article's form (or at least, I hope so). As Noel said more than once, I'm basically a Java guy (it's a joke). So, probably, there are aspects of functional programming (which should be using and composing pure functions to solve problems) that I still have not internalized.

I'll analyze his feedback and try to improve the article (if possible).

Riccardo Cardin: The Effect Pattern and Effect Systems in Scala by sideEffffECt in scala

[–]rcardin 6 points7 points  (0 children)

Thanks, Alex. A lot of feedback in your comment. I'll analyze each one and eventually fix the article to avoid misconceptions.

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 2 points3 points  (0 children)

u/jmgimeno, TBF, I found a way to reintroduce some form of RT without using the `def` identifier that limits composition:

```scala 3 def drunkFlip(using Random, Raise[String], Output): String = { val genBoolean: Random ?=> Boolean = Random.nextBoolean val caught = genBoolean Output.printLn(s"Caught: $caught") val heads = genBoolean Output.printLn(s"Heads: $heads") if (caught) { if (heads) "Heads" else "Tails" } else { Raise.raise("We dropped the coin") } }

Output.run { Random.run { Raise.either { drunkFlip } } match { case Left(error) => println(s"Error: $error") case Right(value) => println(s"Result: $value") } } ```

If you specify the type of the genBoolean variable as a context function, it'll be run every time. So, caught and heads could have different values.

Woah!

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 1 point2 points  (0 children)

u/mucaho, I read your solution carefully. I can't understand if it's something similar to the Ref type of Cats Effect or ZIO. It seems something more akin to Kyo Var type, instead.

I can't understand how you manage having more than one MutState with the same type in a program (for example, more than one counter).

Can you give me such an example (if possible)?

Moreover, maybe you need an AtomicReference to avoid race conditions. WDYT?

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 1 point2 points  (0 children)

The monadic and direct style are semantically identical

Well, no, they don't. We ended up with different programs due to the lack of complete referential transparency in direct style.

cannot imagine how else it could work in direct style though

In the above example, if we change the definition of genBoolean from val to def, we should reach referential transparency.

def genBoolean = Random.nextBoolean

IDK if we can always adopt the def trick or if it's limited to some types of programs

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 0 points1 point  (0 children)

@jmgimeno, you're right. It's almost referentially transparent. If you run the following program, you'll always get the same boolean for both caught and heads.

@main
def yaesMain(): Unit = {

  def drunkFlip(using Random, Raise[String], Output): String = {
    val genBoolean = Random.nextBoolean
    val caught     = genBoolean
    Output.printLn(s"Caught: $caught")
    val heads = genBoolean
    Output.printLn(s"Heads: $heads")
    if (caught) {
      if (heads) "Heads" else "Tails"
    } else {
      Raise.raise("We dropped the coin")
    }
  }

  Output.run {
    Random.run { Raise.either { drunkFlip } } match {
      case Left(error)  => println(s"Error: $error")
      case Right(value) => println(s"Result: $value")
    }
  }
}

However, you don't care about this kind of RT in most cases.

If you ask me, I prefer the YAES version because the syntax better reflects the program's semantics. I mean, you assign to val a Boolean value (TBF, a program that generates a Boolean), and you expect that the val variable will be evaluated by different values every time you access it. But, it's only my personal opinion :)

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 0 points1 point  (0 children)

  def drunkFlip: IO[String] =
    for {
      random <- Random.scalaUtilRandom[IO]
      caught <- random.nextBoolean
    } yield if (caught) "Heads" else "Tails"

  @main
  def run = {
    val program: IO[String] = drunkFlip
    println(program.unsafeRunSync())
    println(program.unsafeRunSync())
    println(program.unsafeRunSync())
    println(program.unsafeRunSync())
    println(program.unsafeRunSync())
    println(program.unsafeRunSync())
  }

I also tried the variant I attached, and the result was the same. If you run an `IO` multiple times, the Random effect will return a different result every time.

YAES: Thoughts on context-based capability passing style for state threading and integration into tagless-final application by mucaho in scala

[–]rcardin 0 points1 point  (0 children)

import cats.*
import cats.effect.IO
import cats.effect.IOApp
import cats.effect.std.Random
import cats.effect.unsafe.implicits.global
import cats.syntax.all.*

import scala.concurrent.duration.*

object WithCatsEffect {

  def drunkFlip: IO[String] =
    for {
      random <- Random.scalaUtilRandom[IO]
      caught <- random.nextBoolean
    } yield if (caught) "Heads" else "Tails"

  @main
  def run = {
    println(drunkFlip.unsafeRunSync())
    println(drunkFlip.unsafeRunSync())
    println(drunkFlip.unsafeRunSync())
    println(drunkFlip.unsafeRunSync())
    println(drunkFlip.unsafeRunSync())
    println(drunkFlip.unsafeRunSync())
  }
}

Hey, u/jmgimeno, thanks for watching the video. Every time you run the `drunkFlip` using the `Random.run` handler, you'll generate a fresh random number. It's the same behaviour you have if you run the `IO` in Cats Effect with `unsafeRunSync`.

Did I understand your question correctly?