00:00:00.060
I want to share a tale before I begin
00:00:03.120
I've given several talks before but I've
00:00:05.220
never looked at my own recording it's
00:00:07.140
because it's too awkward to watch myself
00:00:09.300
talk but today I clicked open one of my
00:00:12.300
recordings because of this whole other
00:00:14.460
story of about how I realized this
00:00:17.640
morning I deleted my flights for today
00:00:19.800
and I had to rework
00:00:22.199
um everything
00:00:24.119
again and then I realized that I when
00:00:28.500
while I was watching the recording I
00:00:30.779
realized that I say so many um when I'm
00:00:33.480
presenting it was a really painful to
00:00:36.480
watch
00:00:37.559
so the takeaway is that if anyone is
00:00:40.620
presenting in the future it is extremely
00:00:43.920
helpful to record and watch yourself
00:00:45.960
first if you have already
00:00:49.559
um another takeaways I'm going to say a
00:00:52.379
lot of um
00:00:53.879
um today so please bear with me
00:00:57.239
all right with that
00:00:59.399
I am my name is Jenny uh
00:01:03.739
I am I work at Shopify and I am from
00:01:08.220
Taiwan but I moved to Toronto Canada
00:01:11.240
later last year and it's been cold
00:01:16.740
um another random fact about me is that
00:01:19.080
I when I come across something
00:01:20.820
interesting I sometimes get too obsessed
00:01:23.700
and make a top cut of it
00:01:26.880
so
00:01:28.979
like functional programming
00:01:31.259
so the hypers up for the topic here is
00:01:33.840
an article from GitHub reading project
00:01:35.700
last year that talks about the rising of
00:01:38.759
functional programming over the past
00:01:40.140
decade
00:01:42.780
even though the term has become popular
00:01:45.140
there are still some common conceptions
00:01:47.640
about functional programming that might
00:01:49.380
be misleading like you have to take math
00:01:52.979
and be able to explain bonad in two
00:01:55.680
minutes
00:01:57.780
but I want to explain in Ruby developers
00:02:02.460
term what functional programming is
00:02:05.880
and even more relevant to us why should
00:02:08.640
we care about it
00:02:11.280
the main point I want to convince you
00:02:13.200
today is that functional programming
00:02:16.020
helps us write better Ruby code
00:02:20.760
and to properly understand what a
00:02:23.640
functional programming is let's first
00:02:25.140
talk about programming paradigms
00:02:27.959
it is a way to classify programming
00:02:30.239
languages based on their features
00:02:33.260
so oftentimes you hear people say Ruby
00:02:36.599
is a object-oriented language because it
00:02:39.959
a lot of its concepts are based on the
00:02:41.940
object-oriented parenting
00:02:43.980
and the functional programming that
00:02:46.560
we're talking about today is another
00:02:48.239
programming paradigm
00:02:50.760
and let's look at the mental model to
00:02:52.860
ease our way into understanding that
00:02:54.959
more
00:02:56.660
for every program we can break them down
00:02:59.640
into three components
00:03:01.680
value behavior and time
00:03:05.160
what does that mean
00:03:06.720
say today we have a program that has
00:03:09.480
this expression uh puts hello world
00:03:13.319
in this example puts is a behavior hello
00:03:17.280
world is the value
00:03:19.580
another example we assigned wmbrv to the
00:03:23.760
variable Faith group
00:03:25.920
called up case on it
00:03:29.879
does the value of a variable is now
00:03:32.280
different in different
00:03:33.980
execution point so we see the time
00:03:37.140
element in between those two expressions
00:03:41.819
to look at our mental model again and
00:03:44.580
try to use the three components to an
00:03:46.620
object-oriented program
00:03:48.420
it will look something like this
00:03:51.239
the time element is within both value
00:03:53.879
and behavior because taking both about
00:03:57.000
both both those two components can
00:03:59.459
change over time and they are all
00:04:02.280
encapsulated into object
00:04:05.040
and we'll have more options so while
00:04:09.000
while each having the same arrangement
00:04:10.920
of those components
00:04:14.879
if we model functional programming with
00:04:17.280
the same three components we have three
00:04:19.979
distinct circles
00:04:22.440
so now let's look at them one by one
00:04:24.840
first of all value
00:04:27.919
to talk about value in functional
00:04:30.540
programming we need to talk about the
00:04:32.220
concept of immutability
00:04:35.340
and the not overly complicated
00:04:37.919
definition of it is don't change a value
00:04:41.340
once you initialize it
00:04:43.740
in Ruby it would be like adding the
00:04:46.320
method freeze on objects like like this
00:04:49.800
example
00:04:52.919
and also in class definition
00:04:55.520
we will discard the attribute writer and
00:04:59.040
attribute ancestor on the instance
00:05:00.840
variables
00:05:01.919
because having them means we're giving
00:05:04.620
open assets to modify the values of the
00:05:07.139
instance variables and we don't want
00:05:08.940
that
00:05:11.940
and why we want immutability
00:05:15.680
from a developer's point of view it is a
00:05:18.960
lot easier to reason about the code if
00:05:21.060
you know that there's a guarantee that
00:05:23.699
the values cannot be changed
00:05:27.419
as an example we Define a struct called
00:05:30.300
flight with an attribute date
00:05:33.180
and I need initialize my flight variable
00:05:36.360
with a certain date and then I share
00:05:39.240
this value with another variable called
00:05:41.639
friends fights
00:05:43.139
later the Flint the French flight
00:05:45.539
changed its state to a day earlier
00:05:48.240
and now when I expect my fight I will
00:05:51.720
see that the date of my fight has also
00:05:54.780
been changed
00:05:57.720
that example was already short one so
00:06:00.360
it's easier to spot the mistake but
00:06:03.240
normally we'll be working with a much
00:06:05.220
larger code base where a variable can be
00:06:07.500
can easily change values across
00:06:10.199
different files
00:06:11.600
and
00:06:13.199
um and it will be painful if if a bug
00:06:15.419
happens in one of those intermediate
00:06:17.820
value states of the variable
00:06:21.780
and the second reason is uh
00:06:25.560
is from the wrong times point of view
00:06:28.440
without the need to worry about a value
00:06:32.360
and if it will be changed later or not
00:06:35.039
it can be a lot easier to support
00:06:37.620
concurrency which I will talk about more
00:06:40.620
in details later
00:06:44.759
okay so next question will be how
00:06:48.960
first uh the new data structure called
00:06:51.300
Data is just released in Ruby 3.2
00:06:55.620
this is the piala introduced it
00:06:58.620
it aims to be a simple imputable value
00:07:01.979
object
00:07:03.060
so instead of using class or shot to
00:07:06.180
Define our object we can now use the
00:07:08.880
data class and a guarantee of
00:07:10.919
immutability will just come out of the
00:07:12.960
box when you use it
00:07:15.740
if you are not Ruby 3.2 yet but still
00:07:20.340
want to try out this idea there's a gem
00:07:23.580
called joist shot that does the same
00:07:25.979
thing for you
00:07:28.080
it is dry shock is part of the dry ruby
00:07:31.860
collection you can visit dry ruby.org to
00:07:34.560
find out more
00:07:35.639
there's
00:07:37.139
um a lot of functional programming ideas
00:07:38.900
in the in the collection not just this
00:07:41.340
one so definitely check it out if you
00:07:43.500
want to dig more into functional
00:07:44.699
programming and how it's realized with
00:07:46.919
Ruby
00:07:48.800
so that concludes the value part of the
00:07:51.660
functional programming paradigm
00:07:53.699
the core idea of it is immutable data
00:07:58.080
next we look at Behavior
00:08:00.740
the key idea here is pure functions
00:08:05.699
a pure function is defined in two
00:08:07.680
conditions first it only depends on its
00:08:10.860
input argument
00:08:12.380
for example this function is not pure
00:08:15.720
because there's there's no input
00:08:17.880
argument but it relies on the time
00:08:20.099
module to produce the value so you'll be
00:08:23.520
you'll be different every time you call
00:08:25.319
it
00:08:26.940
and if we change it a bit and instead of
00:08:30.419
calling a Time module right inside the
00:08:33.180
definition we can pass the timestamp
00:08:36.360
step instead from argument
00:08:39.240
then it's a preview function because as
00:08:41.520
long as argument is the same the result
00:08:43.800
will be the same
00:08:46.080
the second condition is that it does not
00:08:48.300
mutate states that is it has no side
00:08:51.360
effects
00:08:53.160
so this is not a pure function
00:08:55.740
although it satisfy the first condition
00:08:57.920
it has changed the variable stir Forever
00:09:01.920
by appending to exclamation marks to it
00:09:06.779
if we change it to this then it's a pure
00:09:10.019
function
00:09:11.339
because it doesn't modify the stir
00:09:13.500
itself
00:09:15.839
as to why I will read this quote
00:09:19.080
the problem with all languages is
00:09:21.540
they've got all this implicit
00:09:23.160
environment that they carry around with
00:09:24.839
them
00:09:25.560
you want a banana but what you got was a
00:09:28.440
gorilla holding the banana in the entire
00:09:30.839
jungle
00:09:32.459
and this is this code is by Joe
00:09:34.200
Armstrong creator of erlang a functional
00:09:38.220
link a programming language
00:09:41.279
I will highlight the word implicit
00:09:43.260
environment here
00:09:46.080
I think the analogy captures the essence
00:09:49.320
of why pure function is a good thing so
00:09:52.019
here is an effort of illustration
00:09:56.580
suppose the the orange circle is our
00:09:59.760
brain
00:10:00.600
and if we don't have pure functions that
00:10:04.500
means when we look at a function in this
00:10:06.420
case the banana we can't just focus on
00:10:08.880
that but also need to pay attention to a
00:10:11.279
lot of other parts external to this
00:10:14.100
function because if we don't do that we
00:10:16.320
may find ourselves missing certain key
00:10:18.180
aspects of the function or would be
00:10:20.580
modifying some states somewhere else
00:10:22.140
that would have some effects down the
00:10:24.420
road
00:10:25.760
and that would be that can be
00:10:28.680
overwhelming
00:10:30.540
on the other hand if we are able to
00:10:32.640
focus on the function as it is
00:10:36.060
with would be I will have more brain
00:10:38.640
space focus on the details of the
00:10:41.160
function itself
00:10:43.019
and that's more likely to reduce bugs
00:10:47.480
it will also give us a much easier time
00:10:50.160
to write the tests because pure
00:10:52.019
functions are by definition isolated
00:10:57.360
and you also give the reverse the pr
00:11:00.180
reviewers an easier time because now
00:11:02.279
your PR is so small and neat so it's
00:11:05.519
basically a wing wing wing wing
00:11:08.160
situation
00:11:11.160
with that how do we do that
00:11:13.980
the first technique that we can use to
00:11:16.260
produce pure function is
00:11:18.620
parameterization in functional
00:11:20.700
programming's term or
00:11:22.440
dependency injections in object-oriented
00:11:25.620
term
00:11:27.260
this is an example
00:11:29.459
this is a new standard class and we have
00:11:32.399
a subscribe function
00:11:35.399
first inside a function we first check
00:11:37.860
some conditions
00:11:39.240
return false if those conditions are not
00:11:41.820
met
00:11:43.079
and if they do pass you add the email to
00:11:46.140
the mailing list and return to
00:11:50.100
there are some traits that make it not
00:11:53.279
pure
00:11:54.779
first we are calling an outside module
00:11:57.360
to dual validation first
00:12:00.600
and we're also relying on the instance
00:12:02.579
variable mailing list and that is a
00:12:05.640
state that we have no control of
00:12:09.000
to fix it we can move the two things to
00:12:12.180
the parameter list
00:12:14.339
now we don't rely on implicit knowledge
00:12:16.380
of the outside world for this function
00:12:20.279
but there's still a thing that makes it
00:12:23.100
not pure
00:12:24.120
in this in this operation is modifying
00:12:27.660
the state of mailing list
00:12:30.240
and that brings the second technique
00:12:32.820
transformation instead of mutation
00:12:35.940
instead of relying on
00:12:38.600
relying implicitly modifying the mailing
00:12:41.700
list
00:12:42.540
we can just return the new array as a
00:12:44.880
return value and preserve the original
00:12:46.620
value
00:12:48.420
like this
00:12:50.820
now if we pass this new set newsletter
00:12:53.459
class to the two conditions
00:12:55.560
only depends on its agreement check
00:12:59.339
does not mutate States check
00:13:02.459
so that's the behavior part of
00:13:05.040
functional programming
00:13:06.540
the key idea is pure function
00:13:09.899
the last component is time
00:13:12.000
and this is where we talk about
00:13:13.560
concurrency
00:13:16.200
as we've seen before this is how I think
00:13:19.079
about an oo program in my head
00:13:22.860
and if we try to run a ruby Chrome brand
00:13:25.800
concurrently out of the box we cannot
00:13:29.339
there's is this thing called the global
00:13:31.920
interpreter lock that prevents Ruby
00:13:34.740
color from doing so
00:13:36.240
and the reason that we needed this log
00:13:38.339
in the first place is clear safety that
00:13:41.639
is uh there's a risk in messing up the
00:13:44.040
timeline of object mutations thus
00:13:45.959
messing up the program itself
00:13:49.019
the functional programming is quite good
00:13:50.820
at handling this because it separates
00:13:52.940
the time element out of the value and
00:13:57.480
behavior
00:13:59.459
which makes concurrency easy
00:14:02.959
take Elixir uh another functional
00:14:05.880
programming language take elixirs
00:14:08.700
currency model as example
00:14:12.360
um it uses the this actor model so here
00:14:17.339
we Define a process or an actor as a
00:14:20.040
unit of execution they can run
00:14:22.860
concurrently and they they will try to
00:14:24.959
communicate with Azure they can only
00:14:27.300
communicate with each other by sending
00:14:29.399
messages instead of mutating the States
00:14:31.680
of each other
00:14:33.959
and this is what it looks like when
00:14:36.060
running with multiple threads
00:14:38.820
um
00:14:39.860
and one reason that Elixir is uh is is
00:14:43.920
great at this and can easily achieve
00:14:46.620
this state is that it doesn't need to
00:14:49.139
worry about the state's problem and it
00:14:51.000
doesn't need so so it doesn't need a lot
00:14:53.279
like like Ruby does
00:14:56.480
in recent years Ruby has also started to
00:15:00.000
build its own concurrency Obsession it's
00:15:01.800
called Rector
00:15:03.000
Matsu South said so that it's very
00:15:05.639
important
00:15:07.760
this is how we can understand Rector
00:15:10.339
erector is also a unit of of execution
00:15:14.880
but what's different with the Elixir
00:15:17.880
model is that each direct each Vector
00:15:20.820
contains multiple threads whereas a
00:15:23.100
thread in
00:15:24.320
with Elixir it can cannot contain
00:15:28.279
millions of Elixir actors
00:15:32.699
and each factor can has a clear boundary
00:15:37.579
and and is only interpreter lock instead
00:15:40.920
of just one Global lock so now the right
00:15:44.699
the directors can run concurrently of
00:15:47.220
each other while still guarantee that
00:15:49.740
the code inside each Vector is run
00:15:52.260
sequentially
00:15:55.680
and as of Ruby 3.2 Rector is still
00:15:59.940
flagged as experimental but
00:16:03.300
um it's very exciting to to see
00:16:05.880
development in the future
00:16:08.600
okay and that concludes the three
00:16:12.300
components of the functional programming
00:16:14.880
um
00:16:15.720
to recap a functional programming has
00:16:18.600
three uh in three components
00:16:22.440
we looked at the value and the
00:16:24.959
importance of immutable data
00:16:27.420
and we looked at the behavior and the
00:16:29.760
importance of fear function
00:16:32.160
and how with the separation of time we
00:16:34.740
can achieve concurrency
00:16:37.440
but that's I hope I've
00:16:40.759
convinced you that we that by borrowing
00:16:44.339
those ideas from functional programming
00:16:46.019
and cooperating into a rubiko we can
00:16:48.600
make
00:16:49.380
we can write better and more methane
00:16:52.800
thank you
00:16:54.360
the rest will be resources feel free to
00:16:57.660
check it out if you're interested