Keith Bennett

Ruby Lambdas

Object Oriented Design is powerful for organizing software behavior, but without the benefit of lambdas' code-as-data flexibility, it often fails to reduce solutions to their simplest form. Although Ruby's Enumerable functionality is widely appreciated, its lambdas generally are not. This presentation introduces the developer to lambdas, and shows how they can be used to write software that is cleaner, simpler, and more flexible. We'll go through lots of code fragments, exploring diverse ways of exploiting their power and identifying reusable functional design patterns along the way.

RubyConf 2022

00:00:00.000 ready for takeoff
00:00:17.279 I'm Keith Bennett thank you for being
00:00:18.720 here I'm going to talk about lambdas
00:00:20.460 Ruby
00:00:22.020 I'm a ruby not rail is developer
00:00:24.480 um I say that playfully I you know I
00:00:26.640 have nothing against rails but just my
00:00:28.619 career experience hasn't included very
00:00:30.720 much of it I do a lot more of command
00:00:34.320 line applications tools for testing and
00:00:37.380 networking
00:00:39.480 um and um
00:00:42.540 yeah so
00:00:44.820 Ruby not rails but uh maybe in the
00:00:47.399 future rails I don't know
00:00:49.500 I am not a functional programming expert
00:00:51.680 I've just dabbled a little bit in Elixir
00:00:54.840 enclosure and erlang a few years ago I
00:00:57.239 just did some superficial study and
00:00:59.879 really liked it and and tried to bring
00:01:02.039 some of the concepts to my Ruby
00:01:03.660 programming
00:01:06.600 we'll be talking about
00:01:08.220 lambdas obviously what is the Lambda
00:01:10.380 what does it look like what is it good
00:01:12.479 for and how is it used
00:01:16.320 a Lambda is a free-floating function
00:01:19.200 it's not a part of a it's not an
00:01:22.200 instance of a well it's an essence of
00:01:24.119 the length of a proc class but it's it's
00:01:25.860 not really
00:01:27.840 um attached to an object
00:01:30.960 do you remember when you encountered
00:01:32.220 code blocks for the first time
00:01:35.400 do you remember how they were confusing
00:01:36.960 at first
00:01:38.520 if if they weren't confusing then you're
00:01:40.619 smarter than I am because for me they
00:01:42.180 were confusing I was wondering like
00:01:43.320 where does this how does this work but
00:01:45.900 we persevered and we mastered them
00:01:48.900 was it worth the effort
00:01:50.939 of course
00:01:52.860 lambdas are the next step in that
00:01:54.600 progression they're unfamiliar to many
00:01:56.520 of us but mastering them will bring a
00:01:58.979 lot of benefits
00:02:02.220 such as these
00:02:05.399 let's look at the ways to call a Lambda
00:02:07.500 first let's define one
00:02:09.899 can you see my cursor here yes great
00:02:12.060 that's working
00:02:13.860 um so here's a Lambda that takes a
00:02:16.680 single parameter and returns whether or
00:02:18.780 not it's a multiple of two
00:02:20.760 therefore whether or not it's even
00:02:22.920 and we're assigning it to a local
00:02:24.540 variable called is even now
00:02:26.760 unfortunately you can't
00:02:28.860 uh and a local variable name with a
00:02:31.140 question mark that would be nice but you
00:02:32.580 can't so that's
00:02:34.500 a terrible thing that you lose when you
00:02:36.060 use lambdas in local variables
00:02:39.660 um of course the conventional notation
00:02:41.940 is to use the call method by name
00:02:45.360 but there's also an abbreviated notation
00:02:47.580 without parentheses
00:02:51.540 you can also strangely call Lambda by
00:02:54.300 using square brackets but please don't
00:02:56.940 do this because well most of us see
00:02:59.459 square brackets we think there's a
00:03:01.260 collection that we're trying to find
00:03:02.519 something in
00:03:04.739 of course if you're in a community where
00:03:06.480 everybody's doing it and everybody
00:03:07.319 understands it it's a convention then
00:03:09.060 that's fine but usually that's not the
00:03:10.800 case
00:03:12.599 the Casey quality operator the triple
00:03:14.700 equals will also call a Lambda
00:03:17.400 but this too is confusing and I don't
00:03:20.159 recommend it
00:03:22.200 however in a case statement it can be
00:03:24.239 handy
00:03:25.200 if you specify a Lambda in a case
00:03:27.599 statement in the when Clause then that
00:03:30.659 Lambda will be called with the case
00:03:32.159 variable as a parameter
00:03:37.920 a lot of times I'm going to be talking
00:03:39.420 about lambdas and procs but a lot of
00:03:41.940 times it really doesn't matter whether
00:03:44.159 the object in question is a Lambda or
00:03:47.519 anything containing a call method
00:03:50.159 and the best name that I can come up for
00:03:52.860 this broader category is callable
00:03:55.440 anything with a call method
00:03:58.980 including all proc instances lambdas and
00:04:01.440 non-landas
00:04:04.080 modules are classes with a call class
00:04:06.840 method
00:04:07.980 instances of classes having a call
00:04:10.019 instance method
00:04:11.939 and any other object having a call
00:04:13.560 method added to it directly
00:04:17.160 here's an example of a method that you
00:04:18.900 could use to test whether or not
00:04:19.979 something is a callable it simply calls
00:04:21.660 respond to passing it call
00:04:24.020 we create a class with a call class
00:04:27.419 method a class with a call instance
00:04:29.520 method and when we call callable on all
00:04:33.300 these four they all return true so
00:04:34.860 here's just an empty Lambda an empty non
00:04:36.960 Lambda proc a class with a class method
00:04:39.660 call and an instance whose class has an
00:04:43.620 instance method call they're all true so
00:04:46.199 they're all interchangeable when you
00:04:47.520 have a method that's taking a callable
00:04:49.199 and
00:04:50.100 it doesn't have to be a Lambda it can be
00:04:51.900 anything with a call method because of
00:04:53.280 Ruby stuck typing unlike pretty much
00:04:55.139 every other language I think
00:04:58.800 and as a result the dot parenthesis
00:05:00.840 notation can be used for any of these
00:05:02.580 callables not just procs or lambdas
00:05:07.380 if you want to use a Lambda where a code
00:05:08.880 block is expected you just precede it
00:05:10.800 with an ampersand here
00:05:13.919 if you want to use a method where a
00:05:15.600 Lambda is expected you can do this
00:05:22.020 in Ruby versions before 1.9 this was the
00:05:25.199 only way to specify a Lambda
00:05:27.900 notice it's it's really the same as a
00:05:30.840 code block
00:05:32.100 and starting in one night we had the
00:05:34.500 stabby Lambda notation added which we
00:05:36.840 can use this way
00:05:40.320 if there are parameters to the Lambda
00:05:43.440 then this is the way to pass them in the
00:05:45.479 old notation which is the same notation
00:05:49.440 as with code blocks
00:05:51.060 and with a stabby Lambda it's the same
00:05:52.979 notation as method calls
00:05:57.620 self-invoking Anonymous functions it's a
00:05:59.940 fancy name for something that calls
00:06:02.039 itself and is not named um
00:06:04.740 we have three lines of code here and we
00:06:06.720 have them enclosed in a Lambda why would
00:06:09.120 we want to do that isn't that silly why
00:06:10.680 don't we just use those three lines
00:06:11.880 without the Lambda well once in a while
00:06:14.039 it comes in handy for hiding the
00:06:16.680 variable names the local variables from
00:06:18.720 the outer scope
00:06:20.280 and when I work with coffeescript a few
00:06:23.100 years ago they recommended doing this
00:06:24.780 for that very reason
00:06:28.259 the dot parents notation is a little bit
00:06:31.440 interesting in that you really need to
00:06:33.900 use it
00:06:35.100 um
00:06:37.259 um you can't just like with method calls
00:06:39.120 and Ruby normally you don't need
00:06:40.199 parentheses but if you're using the dot
00:06:42.120 parent notation you do here we create a
00:06:44.819 Lambda here we try to execute it but
00:06:48.060 IR Regis Returns the object itself the
00:06:50.160 Lambda object itself
00:06:52.380 and of course if if you use the
00:06:54.479 conventional method name you don't need
00:06:56.100 the parentheses
00:07:00.539 one of the things that's it's kind of
00:07:02.759 confusing the the proc class capital P
00:07:05.100 Roc class
00:07:07.699 can produce instances that are lambdas
00:07:10.440 and instances that are not lambdas
00:07:13.199 and the naming is unfortunate because
00:07:16.340 the name of the class proc is the same
00:07:19.979 as the name of the non-lander proc when
00:07:22.440 we Define it using this
00:07:25.740 so in spoken language if somebody says
00:07:28.500 proc it's really ambiguous
00:07:31.259 and so for that reason I usually use the
00:07:33.900 term non Lambda proc when I mean an
00:07:36.479 instance of the proc class which is not
00:07:38.639 a Lambda
00:07:39.960 and so here we have a Lambda we asked
00:07:42.240 for its class it's proc we ask are you a
00:07:44.460 Lambda this is a method on the proc
00:07:46.319 class and this is Method it says yes
00:07:49.139 here's a proc are you what is your class
00:07:51.900 proc are you a Lambda no
00:07:56.280 let's compare the behavior of lambdas
00:07:59.039 and non-lander procs the return behavior
00:08:01.919 is different a lambda's return returns
00:08:04.500 only from the Lambda and not from the
00:08:06.240 enclosing method or Lambda
00:08:09.060 as an example we have a method Foo we
00:08:11.520 have a Lambda and we execute it right
00:08:13.979 there in place
00:08:15.660 are we going to see this what will be
00:08:18.060 returned from well
00:08:19.800 turns out we do see this so This Lambda
00:08:22.680 returned from itself but not from the
00:08:24.300 foo method
00:08:26.039 in contrast a non-lander proc
00:08:29.099 return returns from its enclosing scope
00:08:31.500 if we do the same thing but with a proc
00:08:33.240 instead of a Lambda we don't see that
00:08:35.279 still in Foo because it has returned
00:08:37.800 from the method
00:08:42.120 already checking
00:08:43.740 uh in case you're not familiar with the
00:08:45.300 term arity it's just a number of
00:08:46.920 parameters passed to a method
00:08:49.200 checking is making sure that the correct
00:08:51.060 number has been passed
00:08:53.100 lambdas have strict arity checking
00:08:55.200 blocks and non-lamba procs do not
00:08:58.680 here's a Lambda that's expecting two
00:09:00.600 arguments if we call it with one we get
00:09:03.180 an error
00:09:04.680 in contrast
00:09:06.300 if we have a proc there's no complaint
00:09:10.440 and if we have a code block
00:09:13.380 then there's also no complaint so if we
00:09:15.899 hit Define this method here which
00:09:18.120 creates two random Point values and
00:09:20.279 passes them to the pass block
00:09:23.760 um if the block that is passed is only
00:09:26.760 expecting one parameter there's no
00:09:28.200 complaint
00:09:29.100 it just uses the first value and
00:09:31.320 substitutes anything else nil for
00:09:33.180 anything else that's missing
00:09:35.540 if we pass to the right number of course
00:09:37.740 it works correctly if we pass it too
00:09:39.480 many it still works correctly but it
00:09:42.420 uses nil for anything missing
00:09:47.279 so oh I just want to say before I go on
00:09:49.860 to that
00:09:51.240 um well I'll wait for that I have
00:09:52.920 another slide lambdas and procs are
00:09:54.959 selfless
00:09:56.700 um if you say puts self you won't get
00:09:59.279 the proc instance that the Lambda is
00:10:01.459 you'll get whatever it happens to be in
00:10:05.459 um and in IRB the name of the enclosing
00:10:07.800 object is main so that's why we see that
00:10:10.260 there same thing with non-lander procs
00:10:14.820 so because of these things
00:10:20.399 because of the differences in behavior
00:10:22.380 of arity checking and return Behavior I
00:10:25.440 believe that lambdas are preferable
00:10:26.640 they're safer they're more restrictive
00:10:28.880 now of course of course if if you need
00:10:31.920 that looser Behavior that's fine but
00:10:34.860 I think we probably rarely do need that
00:10:37.260 behavior
00:10:39.839 unnecessary complexity is our enemy
00:10:42.120 right you don't want a co-worker who's
00:10:43.800 going to make something overly complex
00:10:45.120 because it's going to be harder to
00:10:46.260 understand modify Etc
00:10:48.600 as software developers we strive to
00:10:50.459 maximize the ratio of functionality over
00:10:52.380 complexity we want to maximize the
00:10:54.540 functionality we give our users we want
00:10:56.700 to minimize the complexity that
00:10:59.100 with which we will need to deal as time
00:11:01.320 goes on with this software
00:11:04.620 why do we use local variables to limit
00:11:07.200 their scope why does it improve
00:11:09.420 Simplicity reliability readability
00:11:13.500 yes
00:11:15.779 regarding the number of instance methods
00:11:17.640 in a class you may have encountered a
00:11:19.200 situation where there is just a really
00:11:20.579 large number of instance methods in a
00:11:22.079 class
00:11:23.399 one metric of that complexity is the
00:11:27.060 number of possible Paths of interaction
00:11:29.820 and it turns out there's a formula to
00:11:31.320 calculate that it's the number of
00:11:32.760 methods times the quantity that number
00:11:34.620 minus one
00:11:35.820 so this is a very small class but
00:11:39.779 imagine in your head that it were larger
00:11:41.220 with only five methods we have a
00:11:42.959 complexity value of 20.
00:11:47.160 in my experience it's very often that a
00:11:49.680 method is used by only one other method
00:11:52.980 so let's say as an example two of these
00:11:55.019 methods are used by only one one of them
00:11:58.440 if we could find a way to move those two
00:12:00.420 into that method
00:12:02.880 look at the difference in the complexity
00:12:04.500 metric it's less than a third
00:12:07.079 so of course there'd be a tiny bit of
00:12:09.000 complexity in the in the right side
00:12:10.320 triangle but very little
00:12:12.839 so let's see if there's a way we could
00:12:14.579 do that well it turns out that in Ruby
00:12:16.860 you can Define methods within other
00:12:18.540 methods
00:12:20.519 we have a class C here we have an outer
00:12:22.680 method we just output a message and then
00:12:24.839 we Define another method
00:12:27.779 so we create an instance of that class
00:12:29.399 we call outer fine it's defining the
00:12:31.980 method it says it is
00:12:34.680 so how would you call the inner method
00:12:36.720 of a method well my best guess is that
00:12:38.820 it would be outer dot enter so let's try
00:12:41.160 that
00:12:43.200 doesn't work what's going on
00:12:46.740 well it turns out
00:12:48.420 that inner is just a regular instance
00:12:51.000 method like any other instance method
00:12:53.459 so we can't use methods as inner methods
00:12:56.100 in Ruby we cannot
00:12:58.800 but guess what we can use instead
00:13:01.500 lambdas
00:13:03.120 or proxy
00:13:05.339 um
00:13:06.660 I call it encapsulation light because
00:13:08.339 it's on a very micro level we can use
00:13:10.800 Lambda as a local nested functions here
00:13:13.139 we have a a method and we want to take
00:13:17.040 the two parts of the input and apply the
00:13:19.440 same behavior computation to both of
00:13:21.480 them identical computation so we create
00:13:24.660 a Lambda to do that computation and then
00:13:27.000 call it twice here
00:13:29.519 now in case you're not familiar with
00:13:32.339 multiple return values in Ruby this is
00:13:34.920 the way you do it you just create an
00:13:36.480 array and return the array and then in
00:13:38.940 Ruby you can deconstruct an array by
00:13:41.279 just giving a comma separated list of
00:13:43.079 variable names and it'll put the
00:13:44.700 appropriate array value into the
00:13:46.920 appropriate local variable name
00:13:48.720 variable
00:13:51.540 but let's say there were many lambdas
00:13:53.700 and not only one
00:13:56.459 would you notice anything interesting
00:13:58.139 about the structure of that method
00:14:00.779 you have a method with a lot of little
00:14:03.600 pieces of
00:14:04.980 isolated behaviors in them
00:14:07.380 it's kind of like a class right
00:14:10.500 so a lot of times I'm I'm writing a
00:14:13.320 method and it's getting more and more
00:14:14.940 complex and I say okay this should
00:14:16.200 really be a separate Behavior but I
00:14:17.459 won't put it into an instance method yet
00:14:18.660 I'll put it into a Lambda and I keep
00:14:21.180 going and once in a while it just gets
00:14:23.339 so complex that I realize this really
00:14:24.959 should be a class of its own or
00:14:27.600 sometimes some of them should really be
00:14:29.160 instance methods
00:14:31.200 um
00:14:33.959 so
00:14:35.880 a method with nested landas can easily
00:14:38.279 be converted into a class here with the
00:14:40.560 case we had before we just create the
00:14:42.420 compute part method
00:14:43.980 and um and we call it and this is the
00:14:47.519 way we would call it in practice I would
00:14:49.980 make these class methods so that there
00:14:52.500 would be no need to create an instance
00:14:54.060 it's kind of useless to have an instance
00:14:56.519 there so minus well even be a module
00:15:01.459 and so we have this makes these all
00:15:05.339 class methods of the module or module
00:15:07.560 methods not sure what the right name for
00:15:09.180 that is
00:15:10.160 and then they could be called in the
00:15:12.420 same way
00:15:16.440 a very common use case for lambdas and
00:15:18.540 my experience is formatters I do a lot
00:15:20.279 of command line work and so I don't use
00:15:23.519 CSS for formatting I use printf
00:15:25.860 and so that's what you'll see here
00:15:28.800 um
00:15:29.399 let's say I wanted to produce an output
00:15:31.560 like this here these last three lines if
00:15:34.139 we look at them we see that they follow
00:15:35.579 the same pattern we have a caption a
00:15:37.500 colon and a value
00:15:39.720 so
00:15:41.940 what do we do about that well let's look
00:15:44.279 at the bottom of this first the return
00:15:46.079 value is a multi-line string and because
00:15:48.540 we have the lower level Behavior
00:15:50.579 isolated into a Lambda the reader does
00:15:53.579 not have to bother filtering out that
00:15:55.740 low level implementation to understand
00:15:57.240 what's going on they say okay something
00:15:59.040 is B formatted in some way and here are
00:16:01.500 the values that are being formatted and
00:16:03.720 if they care to see the low level
00:16:05.100 implementation they can go look at it
00:16:06.959 but they probably won't and it saves
00:16:09.420 people time when they're reading
00:16:11.459 um
00:16:14.040 so there are two really good benefits of
00:16:16.740 this one of them is that the code is
00:16:18.000 more dry that is don't repeat yourself
00:16:19.800 and the other is that you're separating
00:16:22.079 high from low level code and in my
00:16:24.540 experience one of the things that makes
00:16:26.100 code the most difficult to understand is
00:16:29.040 when high and low level code are just
00:16:30.600 mixed in together
00:16:32.220 it's really important and helpful to
00:16:34.380 separate them if you can
00:16:38.399 here's another example of using lambdas
00:16:40.740 for um
00:16:42.600 uh well we're using them for formatters
00:16:45.600 but in in a list of interchangeable
00:16:47.519 formatters we have a hash here this is
00:16:50.699 from my Rex gem r-e-x-e which does some
00:16:53.699 stuff to make command line use of Ruby
00:16:55.500 simpler with inputting uh different
00:16:58.500 formats outputting different formats and
00:17:00.300 some other things and um
00:17:03.240 so you can configure this to use
00:17:05.760 different formatters and parsers and so
00:17:08.400 you would give a command line option
00:17:09.860 minus i j or something like that and it
00:17:12.600 would come in and say okay Jace that
00:17:14.280 will be converted into this symbol and
00:17:16.380 then um
00:17:17.579 uh this hash would be something that
00:17:20.160 then the configuration could just fetch
00:17:22.860 the the callable for that symbol and
00:17:26.100 plug that into a variable and then just
00:17:27.660 use that for the remainder of the
00:17:28.919 program
00:17:31.020 same thing with parsers
00:17:33.179 oh and I just wanted to mention here of
00:17:35.640 course these callables these callables
00:17:37.980 all need to have the same interface the
00:17:39.720 same take the same parameter and return
00:17:41.640 the same kind of thing in short they
00:17:44.280 need to be interchangeable
00:17:46.260 and so in in that case we were taking an
00:17:48.840 object of returning a string in the case
00:17:50.460 of parsers we're taking in a string and
00:17:51.900 returning an object
00:17:54.120 and so this is where the configuration
00:17:56.220 will put it all together it would take
00:17:58.200 those formats and then look up the
00:18:01.080 behaviors corresponding to the options
00:18:03.120 and store them in instance variables and
00:18:05.700 then use them later
00:18:09.419 lambdas are handy for Threads when you
00:18:11.640 create a thread and and launch it you
00:18:13.620 pass it a code block but using the
00:18:15.900 Ampersand you can use a Lambda instead
00:18:17.880 and so then you have all the power of
00:18:19.740 lambdas combining and that kind of thing
00:18:22.340 that can be handy at times
00:18:26.460 lambdas are closures so they carry with
00:18:28.799 them the context of the scope in which
00:18:30.780 they were defined so if there's a
00:18:33.600 local variable n which is 15 we can
00:18:36.780 output it now we could pass this to
00:18:39.179 somewhere else in the program and it
00:18:41.220 would still work
00:18:44.039 fortunately or unfortunately you could
00:18:46.140 also modify those values and that could
00:18:48.780 be a problem
00:18:50.160 but if it is a problem you can tell Ruby
00:18:53.340 oops I want this n variable to be a
00:18:56.580 local Lambda variable don't use the
00:18:58.919 enclosing scope
00:19:00.960 and and that works fine
00:19:04.440 lambdas you can call binding on a Lambda
00:19:06.600 and you'll get the binding that contains
00:19:08.100 those local variable definitions and
00:19:09.780 some other information I don't know if
00:19:11.520 that would ever be useful but it's there
00:19:13.320 if you want it
00:19:16.020 private methods are not really private
00:19:18.120 right you can call send to call them if
00:19:20.880 you want something to be really private
00:19:23.220 you could put it in a Lambda
00:19:26.880 and assign it to a local variable
00:19:29.700 and um
00:19:31.679 that would be totally invisible to the
00:19:34.500 outside
00:19:35.400 why would you want to do that I'm not
00:19:37.140 sure you know maybe if you don't want
00:19:39.140 your library users to cheat and use
00:19:41.640 things that you didn't want them to use
00:19:43.260 because you're going to change them
00:19:44.280 later Orlando would work for that
00:19:49.860 unfortunately it also means you can't
00:19:51.480 get to it with a unit test either so if
00:19:54.120 you really really need to unit test this
00:19:55.620 Behavior if it's not enough to test the
00:19:57.900 behavior of the method in which the
00:19:59.280 Lambda lives you really need to test the
00:20:01.200 behavior of the Lambda you're out of
00:20:03.179 luck you probably want to make that a
00:20:05.100 method instead of a Lambda
00:20:08.640 Lambda is a great lightweight event
00:20:10.140 handlers can be the final Lambda and
00:20:13.500 then put in a variable and then pass it
00:20:15.000 or it can just Define it in place here
00:20:17.520 without assigning it to a variable and
00:20:21.179 check out this notation and compare with
00:20:23.340 what it would look like if you were
00:20:24.419 passing a code block
00:20:25.799 it's almost the same
00:20:27.780 the only difference is the parentheses
00:20:29.400 and the arrow right so syntactically
00:20:33.419 um it's it's really no big deal to use a
00:20:36.539 Lambda instead of a block of course the
00:20:38.340 thing your calling needs to be able to
00:20:39.840 deal with it and it will be dealt with
00:20:41.280 differently on that end
00:20:46.140 for a lot of us that come from
00:20:47.340 object-oriented languages other than
00:20:48.960 Ruby we're used to using classes for
00:20:51.120 polymorphism
00:20:52.520 and so we would have classes here in
00:20:55.860 Java you'd need to create an interface
00:20:57.539 defining the even the call method
00:21:01.860 um and um it's a lot of verbosity right
00:21:05.580 compared that with this
00:21:08.580 lambdas are just so simple they're
00:21:11.100 really good in cases where you just need
00:21:12.900 something really simple
00:21:16.559 predicates are functions that return
00:21:18.360 either true or false
00:21:20.100 and we use them a lot in Ruby the select
00:21:23.400 for example
00:21:25.559 um
00:21:26.280 this is a method that
00:21:28.440 takes a filter
00:21:30.539 for messages and only adds to a list
00:21:33.780 those messages that pass that filter
00:21:35.480 this is what it might look like if we
00:21:38.039 used a Lambda as a parameter
00:21:40.320 we have a parameter call filter and we
00:21:42.960 give it a default value which is a
00:21:44.760 Lambda that returns true unconditionally
00:21:46.559 and in other words not filtered at all
00:21:49.679 where we call it we can say do this if
00:21:53.340 the filter returns true
00:21:55.679 that's pretty simple and
00:21:57.919 self-explanatory right if we contrast
00:22:00.659 this with the more conventional use of
00:22:02.280 code blocks
00:22:04.620 first of all there's no mention of the
00:22:06.539 code block and the signature now yes we
00:22:09.059 can specify the name we can specify a
00:22:12.179 name and then use that name but it's
00:22:14.400 conventionally not done usually it's not
00:22:16.080 done
00:22:17.059 and also more importantly look at how
00:22:19.799 this is being called
00:22:21.720 does that give you a clue about what's
00:22:23.940 going on in terms of the the actual
00:22:26.039 logic that it that that it's a filter
00:22:27.780 for example not really it's kind of um
00:22:31.380 obtuse in my opinion
00:22:36.659 furthermore if we need to pass multiple
00:22:38.820 behaviors we certainly can't use code
00:22:41.400 blocks because we only get one code
00:22:42.900 block per method call right so here's an
00:22:47.340 example of something that takes two
00:22:57.120 there's another
00:22:58.440 about the idea of separating high from
00:23:00.480 low-level concerns and also separating
00:23:02.700 just unrelated concerns into different
00:23:04.860 areas of the code
00:23:07.740 this is a method I was working with um
00:23:11.340 uh ingesting Network messages from
00:23:14.220 various sources and then filtering them
00:23:17.039 want to get rid of some of them I only
00:23:18.480 want some to keep and there were
00:23:21.000 different types of messages and
00:23:22.080 different types of behavior to apply to
00:23:23.580 them and the first time I approached
00:23:26.100 this I just wrote it all in one place
00:23:27.480 and I thought
00:23:28.740 there's so much going on here that's so
00:23:30.360 complex if I'm doing two things in the
00:23:33.000 same area of code then my complexity is
00:23:35.880 probably 2 squared right it's probably
00:23:37.320 four but if I can separate them out it
00:23:39.419 might be a quarter is complex and
00:23:41.340 certainly easier to read so I created
00:23:44.159 this buffered enumerable class which
00:23:46.559 just handled the process of receiving
00:23:49.320 the the
00:23:51.299 um the messages and buffering them and
00:23:53.280 then yielding them
00:23:54.620 and within that parameterized the
00:23:58.260 behavior that knew how to fetch the
00:23:59.880 messages
00:24:00.900 and the behavior that notified you in
00:24:03.960 whatever way you wanted that a chunk of
00:24:06.600 messages had been fetched maybe a log
00:24:08.760 maybe a progress indicator or something
00:24:10.740 like that
00:24:12.240 um
00:24:13.860 so lambdas are great for that kind of
00:24:16.020 thing now here's a really weird thing
00:24:18.840 that I learned and I don't know if it
00:24:20.580 really has any use in the real world at
00:24:22.320 all but you can define a class in a
00:24:25.980 Lambda using the conventional class
00:24:28.140 definition notation
00:24:30.919 you could define a class in a method by
00:24:33.360 using the fine class and Define method
00:24:35.280 that kind of thing but if you want it to
00:24:37.200 look as if it were a conventional class
00:24:39.840 definition you could do that in a Lambda
00:24:42.120 as long as that Lambda is not defined
00:24:44.940 inside a method so here we're creating a
00:24:47.700 Lambda and assigning it to a class
00:24:49.020 constant
00:24:50.880 um and if we have a method that caused
00:24:53.220 the Lambda
00:24:55.559 and then
00:24:56.820 call it
00:24:58.400 then that works
00:25:00.919 again I'm not sure if that's useful at
00:25:03.059 all but I thought it was fascinating
00:25:05.039 that you could do that
00:25:09.000 transform chains ETL light again
00:25:13.200 um
00:25:14.760 usually when we work with the numerables
00:25:16.740 we we take um
00:25:18.780 a list of values and iterate over them
00:25:21.299 with the same behavior
00:25:23.280 I'm talking about taking a list of
00:25:25.679 behaviors and iterate over them with a
00:25:28.200 single value
00:25:29.520 actually in in sequence so let's say we
00:25:33.299 have a tripler Lambda and a square or
00:25:35.520 Lambda and we put them in an array
00:25:38.279 and then we have a starting value of
00:25:39.600 four
00:25:41.059 this is called Transformers so we call
00:25:43.980 inject give it that starting value of
00:25:45.840 four
00:25:46.679 and then apply each behavior in
00:25:48.720 succession to the value accumulating a
00:25:50.760 final result
00:25:52.260 so
00:25:53.700 again I'm not so sure you know when or
00:25:55.980 how this would be useful but it's it's
00:25:57.299 really interesting that this can be done
00:25:59.100 and it might come in handy sometime
00:26:03.299 as you work with lambdas more you may
00:26:04.919 find that there's duplication that you
00:26:06.960 would want to resolve here is an example
00:26:09.480 all of these meth all of these lambdas
00:26:11.700 are multiplying n by a factor
00:26:16.919 there are two major ways to deal with
00:26:19.980 this one of them is called partial
00:26:21.299 application
00:26:22.580 which is basically creating a method or
00:26:25.140 Lambda that
00:26:26.760 pre-fills value or values in another
00:26:30.240 Lambda so here's an example we have a
00:26:33.059 Lambda which takes in a factor and it
00:26:35.640 has a Lambda here that it's returning
00:26:37.799 and it's hard coding that factor
00:26:40.200 into this Lambda and by the way this
00:26:42.900 this hard-coded factor is now immutable
00:26:45.679 and that can be handy as well
00:26:48.480 so we can call it with a 3 for example
00:26:50.820 it'll hard code that 3 here and it'll
00:26:53.940 return a Lambda that returns n times
00:26:55.980 three
00:26:57.779 the other way
00:26:59.279 oh and and that outer thing doesn't have
00:27:01.740 to be a Lambda of course it could be a
00:27:02.940 method as well because Lambda is our
00:27:05.460 first class functions um they can be
00:27:07.799 used like any variable
00:27:09.720 and so it can be returned by a method
00:27:13.980 carrying is uh oh let me check the time
00:27:16.740 two minutes
00:27:18.179 carrying is the other way to do that
00:27:21.419 um
00:27:22.620 let's say you have a Lambda that takes
00:27:25.320 two numbers
00:27:26.820 you can pre-fill a three in there by
00:27:29.880 calling dot Curry and passing three to
00:27:32.220 that and then you get that tripler
00:27:34.440 the other thing you can do is if you
00:27:36.600 want to split that out you could just
00:27:38.760 get the return value from Curry and put
00:27:41.279 it in a variable called Courier
00:27:43.380 this just might be simpler to understand
00:27:45.059 it's not something that you might want
00:27:47.220 to do I don't know
00:27:48.600 um and um then you just pass three to
00:27:51.480 that Courier and you get the tripler
00:27:55.980 and we're done thank you for listening
00:27:59.100 um
00:28:06.779 I'll leave my contact information up
00:28:08.940 there for as long as they keep that on
00:28:10.500 feel free to contact me I'd love to hear
00:28:12.779 from you thanks for listening