Composition

Video in TIB AV-Portal: Composition

Formal Metadata

Title
Composition
Title of Series
Number of Parts
67
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this license.
Identifiers
Publisher
Release Date
2016
Language
English
Producer
Confreaks, LLC
Production Place
Cincinnati

Content Metadata

Subject Area
Abstract
Our work as programmers consists largely of problem decomposition and solution recomposition. This talk is interested in how we cobble small units together into cohesive solutions. We'll examine and compare both object and functional composition, using a Haskell-inspired, functional style of Ruby. Along the way, we'll see how good functional principles can improve our object-oriented design, and vice versa.
Loading...
Axiom of choice Mechanism design Functional (mathematics) Type theory Object (grammar) Function (mathematics) Complex (psychology) Self-organization Object (grammar) Mass
Point (geometry) Axiom of choice Functional programming Slide rule Functional (mathematics) Inheritance (object-oriented programming) Observational study Multiplication sign 1 (number) Computer programming Theory Hypothesis Number Mathematics Goodness of fit Different (Kate Ryan album) Object (grammar) Core dump Energy level Monad (category theory) Formal grammar Physical system Area Topology Theory of relativity Inheritance (object-oriented programming) Gradient Expert system Electronic mailing list Core dump Bit Cartesian coordinate system Degree (graph theory) Category of being Word Arithmetic mean Process (computing) Personal digital assistant Function (mathematics) Network topology Video game Right angle Object (grammar) Quicksort Abelian category
Rule of inference Dependent and independent variables Topology Object (grammar) Network topology Electronic mailing list Video game Object (grammar) Social class
Rule of inference Type theory Topology Topology Algebra Object (grammar) Network topology Group theory Right angle Endliche Modelltheorie Algebra Physical system
Classical physics Topology Functional (mathematics) Topology Multiplication sign Direction (geometry) Mereology Data mining Arithmetic mean Goodness of fit Different (Kate Ryan album) Computer configuration Object (grammar) Network topology Right angle Quicksort Algebra Rhombus
Functional (mathematics) Topology Moment (mathematics) Sheaf (mathematics) 1 (number) Grand Unified Theory Disk read-and-write head Sample (statistics) Algebra Object (grammar) Hill differential equation Right angle Object (grammar) Algebra Social class
Axiom of choice State observer Functional programming Topology Functional (mathematics) Context awareness Wrapper (data mining) Algebraic number Combinational logic Expert system Electronic mailing list Equivalence relation Algebra Error message Object (grammar) Software design pattern Hill differential equation Right angle Object (grammar) Quicksort Error message Physical system Social class
Mathematics Standard deviation Functional (mathematics) Constraint (mathematics) Positional notation Function (mathematics) Right angle Circle Mereology Resultant Number
Functional (mathematics) Server (computing) Mobile app Computer file Dependent and independent variables Code Real number MIDI 1 (number) Mereology Web 2.0 Mathematics Positional notation Term (mathematics) Different (Kate Ryan album) Operator (mathematics) String (computer science) Energy level Algebra Social class Programming language Dependent and independent variables Key (cryptography) Software developer Expression Bit Database Cartesian coordinate system Electronic signature Type theory Arithmetic mean Fluid statics Software Integrated development environment Hash function Function (mathematics) Order (biology) Chain output Hill differential equation Right angle Quicksort Middleware Resultant Tuple Abstraction Reverse engineering Asynchronous Transfer Mode
Point (geometry) Email Topology Functional (mathematics) Implementation Server (computing) Building Dependent and independent variables Code Multiplication sign Function (mathematics) Mereology Disk read-and-write head Perspective (visual) Power (physics) Element (mathematics) Web 2.0 Mathematics Positional notation Term (mathematics) String (computer science) Arrow of time Endliche Modelltheorie Nichtlineares Gleichungssystem Hydraulic jump Social class Physical system Dependent and independent variables Torus Topology Block (periodic table) Electronic mailing list Bit Radical (chemistry) Process (computing) Fluid statics String (computer science) Network topology Order (biology) Video game Hill differential equation Right angle Object (grammar) Quicksort Freeware
Filter <Stochastik> Digital filter Topology Multiplication sign Similarity (geometry) Jukebox Drop (liquid) Subset Different (Kate Ryan album) Term (mathematics) String (computer science) Endliche Modelltheorie Algebra Descriptive statistics Default (computer science) Topology Length Electronic mailing list Grand Unified Theory Type theory Process (computing) String (computer science) Network topology Right angle Relief
Topology Digital filter Functional (mathematics) Implementation Topology Right angle Arrow of time Bit Lambda calculus
Torus Point (geometry) Digital filter Functional (mathematics) Topology Matching (graph theory) Multiplication sign Sigma-algebra Combinational logic Bit Parameter (computer programming) System call Category of being Different (Kate Ryan album) String (computer science) Network topology Logic Right angle Binary multiplier Identity management Asynchronous Transfer Mode Lambda calculus Social class
Dataflow Topology Functional (mathematics) Topology Code Electronic mailing list Bit System call Goodness of fit Message passing Term (mathematics) Natural number String (computer science) Lie group Right angle Endliche Modelltheorie Object (grammar) Lambda calculus
Topology Clique-width Topology Algebraic number Bit Function (mathematics) System call Rule of inference Hand fan Revision control Mathematics Sample (statistics) Logic Right angle Object (grammar)
Regulärer Ausdruck <Textverarbeitung> State of matter Multiplication sign Rule of inference Perspective (visual) Field (computer science) Object-oriented programming Term (mathematics) Object (grammar) Configuration space Data structure Physical system Rule of inference Dependent and independent variables Interface (computing) Field (computer science) Line (geometry) Term (mathematics) Affine space System call Type theory Logic Interface (computing) Software testing Right angle Object (grammar) Quicksort Functional (mathematics) Physical system Reading (process)
Point (geometry) Functional (mathematics) Parsing Interior (topology) Formal language Compiler Type theory Word Process (computing) Type theory Function (mathematics) output Right angle Object (grammar) Quicksort output Physical system
Parsing Computer font Functional (mathematics) Parsing Run time (program lifecycle phase) Multiplication sign Chemical equation Bit Type theory Type theory Term (mathematics) String (computer science) output Software testing output Permian Compilation album
Functional (mathematics) Service (economics) Parsing Code State of matter Multiplication sign System administrator Shape (magazine) Field (computer science) Internetworking Single-precision floating-point format Software testing Code refactoring Endliche Modelltheorie Descriptive statistics Physical system Form (programming) User interface Dependent and independent variables Link (knot theory) Projective plane Database Message passing Process (computing) Order (biology) Right angle Object (grammar) Quicksort Clef Writing
Dataflow Parsing Forcing (mathematics) Bit Business object Intermediate language Order (biology) Term (mathematics) String (computer science) Order (biology) Boundary value problem Right angle Boundary value problem Validity (statistics) Message passing Resultant Social class
Point (geometry) Dataflow Functional (mathematics) Implementation System call Parsing Service (economics) State of matter Mountain pass Multiplication sign Field (computer science) Cache (computing) Charge carrier Reduction of order Software testing Information management Dependent and independent variables Link (knot theory) Field (computer science) Bit Sequence Symbol table Message passing Order (biology) Convex hull Right angle Object (grammar) Quicksort Freeware Spacetime
Revision control Type theory Order (biology) Information management Order (biology) Compilation album output Field (computer science) Hill differential equation Right angle Shape (magazine) Maß <Mathematik>
Dataflow Type theory Parsing Order (biology) Goodness of fit State of matter String (computer science) output Right angle Line (geometry) Lie group
Parsing Execution unit Functional (mathematics) Code Multiplication sign Cellular automaton Shape (magazine) Computer Parsing Number Type theory Order (biology) Mathematics Pointer (computer programming) String (computer science) Order (biology) Right angle Extension (kinesiology) Error message Resultant
Context awareness Functional (mathematics) Dependent and independent variables Order (biology) Natural number Operator (mathematics) String (computer science) Energy level Software testing Arrow of time Error message Modulo (jargon) Parsing Beer stein Sound effect Database Mountain pass Message passing Pointer (computer programming) Process (computing) String (computer science) Order (biology) Convex hull Right angle Object (grammar) Quicksort Resultant Abstraction
Slide rule Parsing Beer stein Functional (mathematics) Login Bit Lace Order (biology) Message passing Error message Operator (mathematics) Normed vector space Error message Resultant
Commutative property State observer Functional programming Context awareness Multiplication sign Mereology Formal language Programmer (hardware) Order (biology) Strategy game Arrow of time Diagram Gauß-Fehlerintegral Extension (kinesiology) Error message Multiplication Parsing Mapping Constructor (object-oriented programming) Keyboard shortcut Electronic mailing list Instance (computer science) Sequence Category of being Message passing Arithmetic mean Process (computing) Funktor Chain Right angle Quicksort Resultant Point (geometry) Functional (mathematics) Monad (category theory) Electronic mailing list Theory Funktor Operator (mathematics) Energy level Monad (category theory) Integer Beer stein Surface Login Line (geometry) Word Error message Object (grammar) Diagram
Functional programming Slide rule Service (economics) Monad (category theory) Blog Blog Determinism Monad (category theory) Quicksort Mereology Computer Twitter
Boundary value problem Coma Berenices Object (grammar) Computer programming
cool hey everybody can everyone hear me
good sounds good all right well thanks for coming down y'all I do appreciate it before we get started I do want to say a big massive stinking thanks to rubyconf all the organizers and volunteers everybody they put this thing on it's a ton of work and I'm massively appreciate it also on a personal note I'd like to say thanks for the iron yard where I work for supporting me and also it's all y'all for coming out you had a choice and where to be right now and you're here and I appreciate it today we're gonna talk about composition I hate talks that start with a Webster's defines composition has but I'm gonna do it and here's why I like doing my background I look through Wikipedia and it struck me as very amusing that the the first definition for object and functional composition are exactly the same like not exactly but like isomorphic right except for not to be confused with the other thing that struck me is amusing so I'm gonna take a swing at defining this for myself for me than the thing I'm interested in this talk is a composition means combining
pieces cohesively right it's the thing that we do is program it all the time we look at a complicated problem we figure out how to chip away little bits we find solutions for those little bits and then we reassemble it into a cohesive whole right so that's the thing that I'm interested in trying to understand a little bit more is that that assembly process so at a high level my goals for this talk are like I want to explain what object and functional composition are I'll spend a little time a functional composition kind of assuming that people are less familiar with that area I also want to intentionally confuse the two a bit right I think a lot of times this object-oriented versus functional is is presented as an either/or like it's a choice you can do one way or the other and and implicitly usually like ones right and ones wrong I don't believe that I think these are more similar than they are different and and like people grasping and an elephant in the dark like we're just looking at the same sort of thing through different lenses and want to explore a little bit no lens joke intended yeah and then kind of along the way I'm assuming people here may be a little bit less from other functional stuff so I'd like to expose you to some phrases and terminology some vocabulary that you might not have or might be a little bit different if you want to go and explore some more functional programming on your own so Before we jump into any of that I want to talk about one my favorite subjects me so some background just to kind explain where I'm coming from here I've been doing Ruby for six or seven years now I have been interested in composing little small things for a lot longer than that also I just love that picture but my interesting composition kind of seriously comes from ended up majoring in math going your ad school and and doing my master's thesis on applications of monads to topology don't worry so much if you don't know what those mean that's not terribly important the point is I spent a lot of time thinking about category theory so like a high quick summary of my life there was Legos and then there was topology and then there category theory and then there's Haskell and that gets you current like we're here now so I'm presenting everything kind of through that lens right to borrow a couple phrases I would say that I'm Haskell infected to my functional core I don't hear I see how many people have like looked at some Haskell in that cursory way okay ace number how many people found it somewhat daunting yeah me too right like I have a degree in category theory and the stuffs not easy if you've looked at some Haskell you may have seen things like this oh it's easy yeah like you want to write hello world like let me explain to you what a monad is I like I appreciate that right like legitimately the reason I got into Haskell was because I was interested in category theory in the abstract and was graduating a needed job and so my told me stuff was useful but you don't you don't need this right if you hear anything like this ignore it I actually intended to present some slides that were like things that I learned from grad school in category theory that are important for this talk but then I realized I'm probably going to be short on time and decided to cut the whole thing and I think that's a really good kind of like microcosm you don't need it to understand the day-to-day work that you're doing it's very useful if you have the time which we don't you should look into it more but really like the only thing that I'll mention here is what a category is in case you're not familiar with it so in math speak a category is a bunch of objects some functions between them and the means to compose them and that's it right category theory is if this is all the data you have what can you do so really category theory is the study of composition the genesis of my interest here I'll point out I have a lot more stuff like big fancy words that are nice but I'm
not gonna saying any of them so don't worry about it but come talk to me later if you're interested let's get to composition okay so object composition I'm gonna assume that this is somewhat familiar I'm not an expert here so I will probably give a bad definition also feel free to argue with me after if you're so inclined but just to kind of sketch this and like level set terminology so we have a reference point when we look at list three or functional ends in a second let me quickly go through kind of what I mean when I say these words I'm gonna say that in our old composition refers to one object holding a reference to another and using that to perform its duties somehow I'll have an example if that's not helpful you may have heard like favor object composition over class inheritance somewhere usually when it's talked about in relation to inheritance it's it's it's a has a relationship and said of an is a it's not specialization it's it's a has a let me try to make this a little bit more concrete with some examples I'd like to model a system that represents topologists unsurprisingly and the important thing that I want my topologist to be able to do is tell jokes so here we go right if you're an
object-oriented something this like what is a topologist well it's a noun I used a noun so I probably want a class that seems reasonable and how do I represent a topologist well they need to be able to tell a joke so that's a method and then they'll do it somehow right it's not super important for this presumably the jokes come from somewhere I'm going to assume that it's not the topologist responsibility they'll just remember all of the jokes they've heard over their life but anyway so we had this object we can use it it's fairly straightforward right let's say we have a topologist we'll initialize them by passing look you can pretend the list of topologist jokes is just a static array that's probably pretty accurate I don't know a lot of new topology jokes being written day by day but yeah so here we go right we have a working object and you know you're on it and you get a joke it's an okay joke Peretti with here's a
Klein bottle just for reference it doesn't have an inside or it's all inside or something sorry there are a
couple more of these just go ahead and okay so this is all well and good the important thing though is we want to understand how our system extends right so we have er topologists now let's say we want a model algebraic topology in particular and algebraic topology are kind of notorious for not being terribly interested in things that aren't group theory and maybe have a short attention
span I don't know so here's here's my
swing at modeling an algebraic apologized an algebraic topology is clearly a type of topologist it's right there in the name right so that's a subclass for sure and they have a particular way of telling jokes namely they're gonna have their same collection of jokes but they don't care about a lot of them so they'll just grab one they actually care about reasonably enough and it works fine right nothing wrong with us so if we have another two ologist another joke here we go you ready it's ask me later and we can keep
going right like so if we have a loud topologist just a simple example right they pick their joke in the usual way it's kind of polished but they have a particular style of delivering it where they just shout everything really loudly all the time and again works fine no problem if you've seen nothing is something you already know what problems coming up next and it was kind of the genesis of this part of talk was thinking that you sidestep a lot of sorts problems in the function world but now what happens when I want to write oh I'm sorry I'll skip the joke it's okay this is not a good one again ask me later if you care all right so the problem comes the
classic diamond problem right what happens when you want to write aloud age algebraic topology so we have specialized two different directions and don't have a good means of rika mining those we have options they're both
terrible we don't want to do it so like what what's a different approach then like how can we approach this not through hairiness but through composition I'll mention there a couple ways if you've seen nothing as something may I have one in your head already I'm gonna do a different one I don't claim that it's particularly good but it is an example of object composition so here's here's another thought at this let's take my topologist exactly like I have it right but but the instant that we need to extend it we go alright instead of trying to specialize let's create in
a class and I'm gonna call this class and algebraic so here's how algebraic no longer will an out to brace be responsible for managing jokes instead an algebra just needs to hold on to a topologist friend and when the algebraic has to tell a joke they're gonna ask their topologist friend what jokes they know pick out one that they like and recite it so this is what i mean when i say has a write like that this alder is onto a reference to another person that has the the jokes and that works fine this is the last one because seriously all the other ones I know are like long and you don't care so yeah right this works fine and the important thing here is we layer in the section functionality by using this this object that kind of wraps our original topologists instead of subclasses we can continue in this fashion and I will for a moment you know
our loudness can be layered on by another layer a loudspeaker holds on to a reference to a jokester all I really care about the jokester is they know how to tell a joke and I'll just modify the joke that gets told on the way out and that works fine the nice thing is like with these objects I have the capability of creating objects in my system with all of the combinations and behavior that I care about so that's good right all of these do work some observations
right like because I have this flexibility you know using objects in this way we get to parametrize the behavior and swap it out reasonably well and don't need a different class for each kind of combination of behavior there are definitely some problems with this particular approach though one is like I just wrote these things and sort of have implicitly assumed that algebraic and loudspeaker sort of equivalent wrappers and just haven't right up there's an error here did y'all catch it like it's easy to overlook and I literally just wrote this and it's not complicated the error here is that algebraic needs the object that it wraps to have a full list of jokes that it inspects and loud speaker only exposes a tell joke method right it's an easy mistake to make the the we assumed something about the API is just implicitly from context that's not true so it's still pretty brittle and you know my way of writing this is probably not the way other people will come up with so if we try to make implicit assumptions about how our objects work we're likely to run into errors like this right and this gets worse the more different choices we had to make along the way I'll admit like if you're a design pattern expert you probably have some better tools for managing this and I think those are great and wonderful but I'm kind of interested in you know in the function program that I've done this has never really been an issue right like there's only usually there's just kind of one thing that you can do and you do it and it works it's nice so let's switch gears a minute and kind of talk about function composition and how it works and then see if we can pull some of those benefits back to remiel and so function
composition warning there's gonna be some like high school math here so I apologize if that upsets anyone I'm gonna try and keep it real brief I think we're probably okay so functions you'll remember function constraint here's standard functional tation right F is a like you plug in a value for X and it returns X plus to you so when you plug in a number you just replace it and you get three there's another function I can also plug in numbers I get a result and here's the important part that I actually care about I can compose these things so here's the math notation for composition it's just a circle so the composition F composed with G when I plug in a number it's it's a pipe lining right it's it's a data pipeline first I'm going to take that value and plug it into G and take the result and plug that into F so by definition it's that which you can compute out and I'm not actually interesting the number there so if I
were to write all of this stuff in Haskell here's what it look like and
part of the reason I like Haskell is because a Haskell function is a lot like a math function right they work about the same you know I don't remember functions in math class ever not working because the network was down not a thing you know I've worried about it they're just good so you know we have some type annotations this is what they look like in Haskell I'll leave some of them throughout but the Morton thing is you have a composition operator and it works exactly the same the composition operator in Haskell is a dot which if you think about how ubiquitous those usually are in any programming language should tell you something about how important Haskell things composition is motor works you know exactly the same as math functions right F composed with G is the function that first feeds a value through G takes the result feeds it through F right so by composing those two I get a new function and this is really the key important part right it's it's not just shorthand for not having to write parentheses it's a means of composing more complicated bits of functionality from simpler ones so F composed with G is you know it is a new function and I can use it just as well as any other function I can write out an expression for it because math but I don't need to write I can just describe it in terms of that composition and use it the way I would anything else which is nice interestingly enough composition is itself a function composition is a function that takes a function and a function and gives you a new function and you can puzzle through the types there and make sure that lines up but they you do them in the reverse order and then they line up and go A to C works out I promise so there's some like Haskell notation lest you think this is all just academic and for doing like high school algebra here's kind of what that looks like in a real world like web server sort of thing you know in in the
web server world we can think of our app as a function that receives a request and though the way to read that type signatures it receives a request does some IO and eventually returns a response that IO could be talking to a database writing to a file launching missiles it's very scary we don't know but you know it takes in a request and returns response and doesn't stop middleware for an application right like what middleware does is it takes an existing application and wraps it in some extra functionality so you you know adds something you can think of middleware again as a function that takes in your existing app and gives you back a new app that's a variant of the so you know I've written plenty of code that looks about like this you know my development mode application is taking my base application and run it through a middleware chain where the middleware chain is itself a middleware right I have a collection of functions that take a nap and return an app so I can line them up end-to-end and compose them and get a new piece of middleware that I can apply I've expanded out the type on serve static just for for reference there serve static takes a string it's like the path that would serve static files from and produces the middleware right so if you were to walk all that all the way out it's a function that takes a string and returns a function that takes requests returns at i/o response and then returns another function it's so like oftentimes unpacking those types is actively unhelpful but that's okay right like we can work at this higher level of abstraction and descend when it makes sense this is actually super similar to how a rack works right slight differences where you take an environment hash and get back a tuple of some things but same basic idea and if you have ever been delighted by the way that like rack middleware composes it's because it's just functions mostly I
have some advantages and I'll mention I'm going to skip this to you in the interest of time although I do want to say one thing one of the things that
makes us work as these functions are pure I don't like that term Jessica Kerr has a better terminal like data in data outright these are high school math functions that take about a minute value and that's it right when you have functions like that your life is pretty simple I'll skip this I'll mention kind of more of the advantages of these sort of actual like functional functions as we go along okay so let's like having seen a little bit of Haskell let's consider that topologist modeling problem and see what it would look like in Haskell oftentimes I think the hardest part for me is just figuring out what kind of thing it is that I'm trying to write like so so often in the object-oriented world going in an object I'm gonna start running a class you can kind of jump in if I'm doing this in Haspel I know that my goal is a function but I kinda have to think through like you know for our web server it was natural to have a function that took a request in front of response what's the corresponding thing here and I think it's important to think more about kind of responsibilities right what are the verbs what are the things that your system is doing so in this world while topologist is a multi-faceted being that can do lots of things including topology and not just job telling the thing that I'm interested in is their ability to abrogate all their some experiences and produce a joke which I will model as topologist is a function that takes the list of jokes that they've heard and returns the one joke that they want to tell right now for simplicity sake I'm going to model a joke as a string so really I'm just trying to build a function that takes in the list of strings and returns a string I should again say for folks that have done some haskell I am gonna elide some details about empty lists and shuffling requiring IO and things like that again I will like post completed code with those full notes in it at some point down the road but this take this as a sketch so like here's a possible implementation of the topologist function that's a quibble went to the the one like our object we had before right so given a list of jokes what the trial just does is shuffles lists and takes the head the first element of the list I could rewrite that differently notice I am taking the output of one function and feeding it through another so I could write that thusly right or equivalently and I feel like that goes backwards from the way I wanna think about a lot of times so I will often for the purpose of this talk write a double arrow as just a reverse composition just composition in the other order so I could also write it this way here's the powerful change in perspective I have described this topologist function by saying what it does to a value I would like to get away from that where possible and if you think about high school math class where you just like cancel outside of an equation we can do the same thing here in a way this is totally equivalent to writing this a topologist is the function that you get by chaining first you to shuffle then you do head this style is called point free notation also pointless if you're feeling snarky right it's just we don't talk about values we talk about building blocks and composed and I find that illuminating okay so there's our topologies function it is I think as simple as it possibly could be right there's very little ceremony here so let's keep going right we want to write our algebraic topologies here's what that could look like same
same basic type Ray takes a list of strings we're trying to string the one little wrinkle here is that our algebraic apologized cares about a subset of jokes and filters and down filter is just like a select on a ruby array and again I have written this in terms of the jokes but I really can kind of drop that and say an algebraic topology so what they do is they follow this three-step process of filtering down their jokes then shuffling then picking one it's a high-level description of what's happening and I find that helpful because it really illuminates the similarity and differences between a topologist and an algebraic topology right those are structurally super similar and that kind of throws into relief the difference right I'm my default topologist is missing something here so if I want to unify these right if I want one thing that can produce all of these different kinds of behaviors it becomes fairly clear what to do right I mean something that looks like this a topologist is
just a person that thinks everything's funny all the time that's not accurate my model has gone wildly astray I'm sorry but yeah this was like there we see the structural similarity and it kind of makes it clear what to do next
all right this is the pipeline that we have for all of these so if I want to create a topologist well I'll need to supply the criteria they use for judging a joke but once we do there's my pipeline right it's exactly thing I've written thus far so kind of yeah from there we can recover our original implementation I should say these are the back slash arrow thing is a lambda think think of stabby lambda in Ruby so I can kind of recover my original definitions but importantly I have this higher-level make topologist thing and conceptually I'm thinking of that as it's a function that takes in a sense of humor and returns a topologist really what that is is this thing which again is I think a bit less enlightening and we can keep going right allowed to Paula gist same
idea where they have a similar pipeline but ended up shouting it when they're done and so if I wanted to make my general make topologist function also be able to produce this thing well I just need to extend my pipeline so I could do
that right now I'm thinking to make topologies as a function that takes and a sense of humor and a mode of delivering jokes and produces the topologist that has those properties so that's the actual type but ignore that right and you know it's fairly straightforward to write I just I'll need to parametrize east apologist by those two bits of behavior and I can do that I'll point out Hospital has some nice T's for defining functions like this the function that takes the value and just returns it is the identity function just ID so I'll write that in some places so I like this right like we get everything we need we have all different kind of behavior and I'm not kind of I'm free to reuse them in any combination that I want it's nice so I'd like to start calling this back towards Ruby my first thought is like we could just try and transliterate some of this so let's look at that very briefly if
you had a function like this that just multiplies by two and Haskell world I think the closest just like transliteration of this function to Ruby would be its lambda right there's no classes anywhere it's just a be lambda the returns if you haven't used lambda as much the way you use them is you can call the call method I'm apologizing in advance for how many times I'm about to say call VI you can call call and it executes the function or it evaluates it that's fine you know because there's something a little more involved the function takes three arguments the interesting thing in Haskell this is this is actually a function that takes an argument and returns a function that takes an argument and returns a function that takes an argument I've lost how many times I said that but you get the idea so the actual transliteration of this would be something like this which is horrible and I don't recommend writing in Ruby like all your co-workers late you but it works great and instantly I found this out recently you can curry procs one fact anyways so while while this match is kind of the letter of what we I don't think it captures the spirit of the solution that ended up with so let's take a step back what I want is a
function right yeah so right here here's the literal one it's a little bit hard to read because I'm mixing procs versus methods and so it doesn't flow right to left but it's you can convince yourself that's equivalent but I think that misses the spirit of the solution right
again what I ended up with was a function that I can parameterize a sense of humor and a delivery method and I get a topologist back so I still wanna think about a topologist as an object here's my swing and modeling that let's say every topologist has their own sense of humor and delivery right this is the object composition solution that I prefer and that you may have jumped towards initially and when I initialize my topologist I will provide them with that sense of humor and delivery that they'll hang on to and use to do the thing that they need to do I'm gonna freeze things because I've been doing too much Haskell but it's good policy just in general if you can so then like when the topologist needs to do its thing it's thing like I'm still trying to pretend that it's function right still trying to keep things as much possible so I will define a call method on this topologist that takes in a list of jokes and does the exact same behavior so isomorphic right but this feels a lot I'm just more Ruby ish to me right like this feels more natural and natural Ruby code base and I think there are some advantages to the solution here's what this looks like in practice right when I make my topologist my allowed apologist I can kind of pass in like here's your sense of humor and delivery method just using some stabby lambdas again because those are functions I guess another advantages you know when I was thinking of that loud topologist the very outset I thought it was a specialization I thought of it in terms of allowed to Paula gist is a topologist with a particular kind of delivery with the set up that becomes pretty easy to write right I can define
a width method that just gives me back a new version of the same object doesn't doesn't manipulate the existing objects provides a new one with whatever overrides I want to provide and that would let me write something like this allowed apologist is a topologist with a particular kind of delivery that's exactly the thing that I was thinking when I started I don't like that a lot better also notably right the only thing I care
about about delivery and sense of humor is that they have a call method right procs have that but they're not the only
things in particular I can write anything I want it has a call method and pass it in right so if I want to think about a sense of humor as an object I can alright if I don't want to nest some complicated logic there I can but it's still kind of API compatible with a prog so I'm free to mix and match them so you know I can write an object that represents the sense of humor that an algebraic has and create topologists with that a bit of behavior injected in it's nice so kind of trying to package that up into some rules like I'm not a huge fan of rules but like if you want
to try that sort of approach here I think are some some like guidelines to follow so the object in a system like this right these functional objects all have a defined collection of fields and the state is entirely expressible in terms of those fields they're immutable right the only time that you you can't update things you only update by you update you create a new object but you kind of only ever update to reconfigure anyway so that's not a huge performance problem really these objects are just kind of steps in your data pipeline so you tend to need relatively few of them and you know if your objects are being functional they should respond to call to perform their primary sponsibility you get a lot of benefits working this way right in a system that follows those sorts of rules all of our objects have a defined responsibility right they have one public method called call there's really only one thing that you can do with them and there's not much a way to confuse it right they have a predictable interface the only thing that you care about is what type of thing don't need affinity nin and what type of thing do I get out they're immutable nice inject will dependency use easy to configure like all of these nice things and this
is like this is not new I just I think it's compelling that again starting from a functional perspective you end up writing object-oriented code in a way that a lot of object reading people just naturally somewhat upon anyways the really nice thing I think like when you have a system written this way because all of your objects are so predicted they're easy to swap out right like I can plug in a proc somewhere and I can mock out things very simply if I want to move logic around I am confident that there aren't side-effects anywhere so I can just snap a piece off plug it back in as long as the structure is correct right like as long as all the types line up I just move things around all willy-nilly it feels like playing with LEGOs that's great that's me tiny now
like I've said types a lot and I think
it's reasonable like if you haven't used a strongly typed language you you may have some objections here I do not necessarily mean anything about a compiler here I'll point out like if you've been doing Ruby you've been thinking about these sorts of things just these maybe aren't the words that you use to describe what you've been thinking about you know if you've ever written a method like this what you're doing is you're starting with requests you're feeding it through a parse input function you're feeding that through a per system unction and you're feeding that through a success without a functionally this is a composition whether you have thought about it as such or not and you were assuming some things along the way right you're assuming that whatever parse input returns is something that persist can take so I would prefer to be explicit about that right like let's name those things parse in put takes request returns post process takes a post and returns an int and the process that we're following here is chaining those those individual steps one after another now you probably don't quite think about it this way like you you don't typically think about types when you're Ruby you think about duck types but you know on
balance ends up being fairly somewhere you don't really care that parse input returns a post so much as you care that parse input returns a thing that has all of the methods that persist means whatever those happen to be so really you're kind of thinking of it as a persistable and that's super-powerful or that that's a thing that's actually kind of harder to emulate ask a lot of times but you know still structurally somewhat similar like whether or not you want to think about these types your functions do you have things that they can accept in things that they can't and you've thought about ballot violations of this all the time you know if you change up your function definition and something like this where my persist function might give me back an ID but might give me back nil then I've broken my pipeline it's something downstream can break you may not have thought of it in terms of these functions no compose anymore but melons that's what happens right the problem is now persist maybe gives you an end but maybe gives you nil and that can cause some problems whether that be a compile time or runtime who cares again in the interest of time I'm going
to skip some of this testing stuff but yeah so don't don't shy away from types I don't mean that you need all of this at compile time just there are latent types in your functions and I think it pays to think a little bit more carefully about them let's try and look at kind of a real-world example so
here's some code that I pulled out of a project that I was working on for the Peace Corps called meddling the basic goal of the system is you know volunteers out on the field don't often have reliable internet access so we have like text in like hey I need Tylenol and bandages and we'll put in a system so this is some code that I have written a long time ago this is a service object that does that taking a text message and placing an order it's not bad right like if you're writing code like this awesome there's some big wins here right there's a nice like clear single responsibility because there's only one public method high-level description of what the thing does decent names for everything right I do have this extracted and not in like a model somewhere so I can reuse it and I needed to write our admins needed a tester where they could just knock out one it's like oh I've got an object for that it's great ready to go but our anuses problems kind of maintaining this kind of twofold one this is not in any way shape or form isolator from the database so eventually my test started getting real slow because I couldn't see the dependencies to swap them out I also couldn't see the interdependencies between these individual steps in the process so down the road when we made a web interface and I needed to pull out the last three steps that ended up being way harder than it seemed like it should be because of hidden interdependencies right my
parse message set some state that some but not all of the later functions needed and that just wasn't ever clear so refactoring was sort of a pain I'd like to get away from that right I get I want Legos right just little pieces I can move around so like I think the big lesson learned here was that you know I am composing things like same sort of idea these steps run one at a time and there's data that's generated in one that's fed into late but it is completely implicit and when your composition is implicit so are the
boundaries and so are the values flowing between us boundaries and that can make it really hard to reason about what's happening I always prefer to make those things explicit even if it's a few more letters right it's worth it so let's be clear about what's happening and let's try and name those intermediate steps sometimes that requires making up a new class for your intermediate representation that's fine so let's let's think about this in terms of a pipeline you know maybe it's not clear maybe this forces you to clarify your thought a bit but here's a stab at a pipeline or I start with an SMS I convert that into a partial result that has some data I build out an actual like order domain object from that and then do some other things that seem sketchy come back to that and then finally when all is said and done and I've placed the order I convert it to a string that the user can see okay I want to write something that looks like that and I can do that right like I can just be explicit so that works right this is the
idea it's I'm for it has one Ruby though not Lisp so this looks bad and I'd like to be a little bit like you know again the data flow seems backwards you know I'd like to be a little bit more explicit about what's happening here so let me try this you see how you all feel about let's say that let's say that what
this does like what this order place or does when you call it oh I should say I'm sorry I should say the the fields there is just shorthand for my like every method has some adder readers and an initialize method that supplies them and then freezes and fields there's a shorthand for like these are the fields that I parametrize in this object usually like for these it's here the dependencies of the thing has so like when this thing runs here's what it does first off it takes an SMS so I only need one of these I always feed a new objects through I'm not holding on to a reference I'm just gonna feed it through that pipeline there's a tiny little bit of cleverness here but you know taking a look at the compose method what that does is composing a sequence of steps gives you back the function that applies the methods one at a time I have a little bit of cleverness to let me write a symbol naming the method and then pulling out the method but remember all of these objects are frozen so all of those methods are really just kind of state free and this is totally fine to do I'm not gonna not gonna cause any confusion hopefully for reference and for saving space I will probably write reduce from here on out if you're not comfortable with reduced style stuff don't worry it's the same thing just saving some space thing so that's equivalent right maybe not a huge wind it is literally equivalent but just here's a starting point from here like the the implementation of the individual steps your parse message your build order would look something like this right they they take an object they return an object that's it I guess I just sort of a liar right I have these dependencies that I've injected in and at some point I will need to say things to the database but at least they are a little bit easier test because they're a little more explicit now that's nice but I still I'm primarily interested in that mainline flow of data through this service object here's roughly what a test could look
like right you may inject in some dependencies some of them may be service objects that are you know proc compatible they don't all have to be it's fine but the main thing you hear about is like when you pass in a message here's the response you get and and possibly my dependencies receive some messages along the way so that's that's the thing that I'm primarily interested in is you know with this
setup when I want to extract out just the order place right just those last three steps it's almost trivial to do so
right here's the extracted version now I have a new dependency my sms order Placer it depends on a base order Placer that will be provided somehow I'm not gonna worry about it right now and my pipeline is just composing those through there is a little wrinkle in that I'm now not naming the thing it's an actual like order placer is an actual factual proc so my little compose needs to respect that and just call it if it's already callable but other than that like everything is unchanged and the only thing I had to do right the thing is important there is the order placer has the same shape right has the same input and output types as those last three steps to compose so it swaps in and I don't have to think about anything else the order placer itself is sorry
somewhat similar it is literally just copy over the last three steps right everything state free so again as long as the input and outputs line up it all works feels like like us it's great so that's all well and good there is one kind of
important lie that I've told along the way that right this is all talking about what happens when all of your types line up and everything composed in end is nice right if I have a nice clean flow of data throughout then I can compose the whole thing and that's good that never happens though like ever what what
actually happens is stuff fails stuff fails all the time all right any number of computations may fail to produce value some kind of error or something let me model that the simplest way I know how which is when a when one of these steps fails is just going to return nil so right my types are now different along the way maybe I have a parser well maybe I haven't order maybe not it could all be nil so right the shapes don't stack up anymore right I end up with some extension that sometimes is a partial at some times ISM and I still want to get from my initial SMS to the end result I can't do anything about the fact that something could have failed along the way right I could end up with a nil that's inevitable but if nothing failed along the way I would like my original value back right but I can do this yeah think about I can apply my first function just like normal and then I'll end up with well one of two things either I haven't no or I don't if I don't cool I can figure through the next step next step next step if I have a nil also cool I just need to pass it forward right if anywhere along the way I ended up with a nil just keep that no I want the end result of this composition to be nil here that is in code the only change is
you know as we're composing these methods we look at the result from the previous step and respect the fact that it might be nil if it is cool we end up with a nil if not we call the next step along the way right and the only thing that needs to change in my actual individual steps is following
I can just return right if I didn't have a result if something went wrong I return nil and I'm done and I can trust my composition operator to halt the process there right the individual steps don't need to worry about that higher level context again I can validate what that was some tests all right if I passing an invalid message then I won't even get so far as calling the like the database say border thing so I'm sort of like I have recreated error handling but purely out of function composition right that's the powerful thing about these abstractions I don't need a lot else right I just need to compose functions let's crank that up a notch right realistically when your functions error you probably want to know why so there is a thing for that
it's called either usually in the Haskell world so the idea behind either is when something goes wrong you're gonna return some object that represents that I'm gonna say a string which is like an error message so along the way each step of this process needs to acknowledge effect right it could return an error message or it could return the actual value I'll need to be a little careful here to differentiate between a successful string value and an error string value but modulo that it's the same right i I have these cross arrows where I don't quite have a purse result something could have gone wrong I don't quite have an order something could have gone wrong but it's okay because there's still a sort of natural thing to do right if I have an error at any step along the way propagate it forward if not unwrap the value and feed it through so I can still get from there to there here it is encode right for me I'm
representing an either as a struct so I can differentiate between a value in an error and then you know my functions just need to return those enriched values the actual composition operator
also needs to adjust a little bit but not a ton the thing that's different now is my result could be either an error or an actual value if it's an error cool pass it forward if not unwrap it feed it through the next step along the pipeline I have a similar example with logging
that I'll post in the slides where like the log or kind of builds up a string as
it goes or built
arrey accumulates an array of messages but I want to kind of like in these last couple minutes here try and give a name
to the commonality between all of these like what so some baseline observations all of
these things air handling logging more a whole lot we can build entirely pure functions that we don't need any sort of extra language constructs and every single one is just defining a slightly different means of composing functions I don't use instance methods anywhere other than the cleverness about method which isn't really necessary so this is really like a generic thing and what I probably should have written is like compose using maybe a compose using either a compose using any of these different strategies so let's here's how a category theorist sees all this stuff what we have one that I need examples is I don't know why that's a larger arrow that's irritating I'm very sorry we like we had these composition pipe lines right and kind of sitting above each of them we have these values with extra context and this could represent a whole number of things right like like MA could represent either an actual a value or an air object it could represent a list of a objects it could represent a promise that will eventually resolve to an a object or maybe air who cares a probability there's a whole bunch there's a whole bunch this is scratching the surface but the commonality is here are we can kind of play around with composition right like if we have sequences of arrows oftentimes what we'll find is if we have these kinda like ground level functions we can lift them to this extended context the word for that just for your reference if you ever talk to a function programmer is to say that M is a functor right if you can lift these downstairs values upstairs M as a functor that process of moving up is called lift or map and an exercise if you wanna think through this lists are a functor and map the operation moving upstairs is map like the function on lists it's good exercise similarly some commonalities along the way each step we had some means of embedding our kind of ground-level value in this extended context right you know if we have a C then that is also a C or an error if we have a C there's an obvious promise that we'll just immediately resolve to that C there's kind of always that extra thing these lines in functional programming parlance are called returned which is immensely confusing or pure and an important thing about this like this diagram is commutative and what that means is like if you think about these are just paths we have lots of ways of getting from A to M E but the nice thing is it doesn't matter we can do whatever we want and be guaranteed that no matter how we compose our functions we'll always end up with the same results we don't have to think about it which is nice I could go this way I can go that way it's the same thing so it makes sense to talk about the arrow from one point to another and those are fine really the thing that we saw over and over in these examples was we often end up with these kind of functions that return extended contact right functions that might error functions that return a promise and we want to chain promises together lots of those the important thing that we saw throughout those examples was that we had some way of taking those functions and still cross composing them right even though I don't quite line up we still could kind of promote and and fudge and lift somehow and end up with a chain of values flowing along in this higher-level context the functional programming word for that is a monad if you can do that that's what a monad is the this moving up operation is called bind written in Haskell this way and again list is also a monad I think it's instructive to think for you like what what does that operation look like when you're talking about lists so I'm at time some parting thoughts here if you've heard things like this I think they're garbage I'm sorry that someone tried to harass you with these the things like it's an abstract thing right there they're hard to define but really
a monad the the the way I think of it as monad is just a way to extend a computation composability compose ibly they are about composition and that's it if you can cross compose you probably have a monad if you have a monad you can cross compose that's it so yeah some some final parting thoughts I'm sorry that this ended up being a
monad tutorial I feel like those are overdone but I hope it was helpful if you want to try the style of ruby stuff out on your own I wrote a lot of these examples to be hopefully explicit and understandable in this context but don't reinvent the wheel there are some gems out there that will help you with things like this also if you want to do like a service layer I think it's a great place to start because a service layers are all about verbs and functional program is good at that you probably do want some sort of ioc container that was exists also dry or be in general is largely written in a style very similar to this so i think it's instructive to read through some of those gems i'll be posting a blog post and references for these slides later so if you are interested follow me on twitter or something i'm talking later so yeah keep in mind how you compose i think is at
least as important as the individual objects that you're composing so just as you assemble your programs do take care to be explicit about how things compose intentional about what those boundaries and values are and you'll end up with something that's pretty nice and extensible that's it for me
Loading...
Feedback

Timings

  495 ms - page object

Version

AV-Portal 3.19.2 (70adb5fbc8bbcafb435210ef7d62ffee973cf172)
hidden