0% found this document useful (0 votes)
86 views5 pages

21 Intro To Effects

This document provides an introduction to modeling side effects using the effect pattern in functional programming. It discusses how Option and Future can be modeled as effects in Scala, and how to define a custom MyIO type to capture arbitrary side effects. Finally, it introduces a simplified version of ZIO to model effects with error handling and environment requirements.

Uploaded by

Gamba Mercenaria
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
86 views5 pages

21 Intro To Effects

This document provides an introduction to modeling side effects using the effect pattern in functional programming. It discusses how Option and Future can be modeled as effects in Scala, and how to define a custom MyIO type to capture arbitrary side effects. Finally, it introduces a simplified version of ZIO to model effects with error handling and environment requirements.

Uploaded by

Gamba Mercenaria
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

Introduction to Effects

[email protected]

2023

Index
• Pure functional programming
• The Effect Pattern
– Option as an effect
– Future as an effect
• Capturing Arbitrary Side Effects as an Effect
– MyIO
– MyZIO

Pure funcional programming (recap)


• The program is a big expression that computes a value
• Referential transparency: we can replace an expression with its value
• Expressions performing side effects are not referentially transparent
• A side effect is everything not captured in the returned value
– printing to the console
– modifying a global variable
– ...

The Effect Pattern


• An effect is a data structure that represents a computational concept
such as
– absence of value
– failure
– I/O
• This data structure allows us to express computations with these concepts
without losing referential transparency

1
– instead of performing the effect we describe it

The Effect Pattern


• An effect is a data structure with the following properties:
– it describes the kind of computational concept it represents
– it describes the type of value it can produce
– it separates the description of the effect from its execution, when
externally observable side effects are involved

Option as a Effect
• Scala allows the use of null values to represent missing values
– But then, the programmer is required to check if a reference is null
before using it, or a NullPointerException will be thrown at runtime
• As we know, Scala has another way to represent optionality, which is the
Option type:
enum Option[+A]:
case Some(a: A)
case None
• Is Option[A] an effect?

Option as a Effect
• Does Option[A] describe the kind of computational concept it represents?
– Yes, it represents the absence of a value
• Does Option[A] describe the type of value it can produce?
– Yes, if there is a value it will be of type A
• Are externally visible side effects involved?
– No, there are no externally visible side effects involved.
• So, Option[A] is an effect !!!

Future as a Effect
• Scala defines the type Future[A] to represent asynchronous compu-
tations that may compute a value of type A or fail with an exception
• Is Future[A] an effect?

2
– It describe the concept of asynchronicity
– If values are produced they are of type A
– In this case, effects are required (allocating and executing threads),
but the execution of this effects is immediate not separated from its
description
• So Futures are not effects !!!

Future as a Effect
• Being not effects, they break referential transparency
• Consider this program
val twice =
Future(println("Hello"))
.flatMap(_ => Future(println("Hello")))
which prints “Hello” twice, but this simple refactoring prints “Hello” only
once
val printHello = Future(println("Hello"))
val twice = printHello.flatMap(_ => printHello)

Capturing Arbitrary Side Effects as an Effect


• Let’s defines a type to capture side-effects:
class MyIO[A](val unsafeRun: () => A):

def map[B](f: A => B): MyIO[B] =


new MyIO(() => f(unsafeRun()))

def flatMap[B](f: A => MyIO[B]): MyIO[B] =


new MyIO(() =>
val nextIO = f(unsafeRun())
nextIO.unsafeRun()
)

object MyIO:
def apply[A](a: => A): MyIO[A] =
new MyIO(() => a)
• Is MyIO[A] an effect?
– It describes the computational concept of side effects
– If values are produced they are of type A

3
– In this case, if effects are required we separate the description of the
computations from its execution

Capturing Arbitrary Side Effects as an Effect


• Let’s define a program to understand better this separation:
object MyIOProgram:

def twice =
MyIO(println("Hello"))
.flatMap(_ => MyIO(println("Hello")))

def alsoTwice =
val hello = MyIO(println("Hello"))
hello.flatMap(_ => hello)

@main def run() =


twice.unsafeRun()

Exercises
• Create some MyIOs which:
1. measure the current type of the system
2. measure the duration of a computation
– use exercise 1
– use map/flatMap combinations of MyIO
3. read something from console
– use scala.io.StdIn
4. print something to the console (e.g. “what’s your name”), then read,
then print a welcome message

A Simplified ZIO
• In the MyIO type we’ve defined so far, we only have control over the result
type when the computation succeeds
• When the computation fails, we do not have a functional way to communi-
cate the type of the error.
• And the computation does not have requirements, so let’s also add an
channel for the needed environment

4
case class MyZIO[R, E, A](unsafeRun: R => Either[E, A]):
def map[B](f: A => B): MyZIO[R, E, B] =
MyZIO { r => unsafeRun(r) match
case Left(e) => Left(e)
case Right(a) => Right(f(a))
}

def flatMap[B](f: A => MyZIO[R, E, B]): MyZIO[R, E, B] =


MyZIO { r => unsafeRun(r) match
case Left(e) => Left(e)
case Right(a) => f(a).unsafeRun(r)
}

A Simplified ZIO
• And, let’s consider the variances of the type parameters:
case class MyZIO[-R, +E, +A](unsafeRun: R => Either[E, A]):
def map[B](f: A => B): MyZIO[R, E, B] =
MyZIO { r => unsafeRun(r) match
case Left(e) => Left(e)
case Right(a) => Right(f(a))
}

def flatMap[R1 <: R, E1 >: E, B](f: A => MyZIO[R1, E1, B]): MyZIO[R1, E1, B] =
MyZIO { r => unsafeRun(r) match
case Left(e) => Left(e)
case Right(a) => f(a).unsafeRun(r)
}
• The real implementation is not like this, but this implementation gives us
a mental model to better understand it.

Bibliography
• J. De Goes and A. Fraser. Zionomicon. Gumroad, TBP. https://www.zi
onomicon.com/
• D. Ciocîrlan. “ZIO 2.0 Course”. https://rockthejvm.com(Access: 2022-10-
10)
• A. Rosien. Essential Effects. Gumroad, TBP. https://essentialeffects.dev/
• M. Pilquist, R. Bjarnason, and P. Chiusano. Functional Programming in
Scala, Second Edition. Manning, TBP.

You might also like