Functional Programming for the Object-Oriented Programmer. Brian Marick For more about what the book covers, see the sample PDF. Once upon a time, object-oriented programming was a radical . In the PDF version, links within a book appear as colored text, and links to. Functional Programming for the Object-Oriented Programmer by Brian Marick ( terney.info) terney.info~rwh/plbook/terney.info
|Language:||English, Spanish, Japanese|
|Distribution:||Free* [*Registration Required]|
old relationships between functional and object-oriented program‐ ming. There's only so much you . read because it obscures the programmer's intent. We don't want to . of assets and render the BalanceSheet to a PDF report. If the imple‐. terney.info - Download as PDF File .pdf), hard to read because it obscures the programmer's intent. but first let's take a. Functional Programming for the Object Oriented Programmer. 24 August This book, written by Brian Marick, is important. Indeed, it may.
You simply write small functions that can be applied in a number of areas. You'll probably end up re-inventing the wheel though, as most functional languages come with most of the essential functional constructs included in the standard library. If anything, functional programming is more beginner friendly, because you don't end up dealing with mutable state and you can easily work on the level of abstraction that you're most comfortable with you're just composing ever more advanced functions from simpler ones.
Most of these are not not what a "reasonable programmer" would do. An unreasonable programmer would be able to screw up on any paradigm. You'd probably have even more ways to screw up like this with an OO language, creating classes for recursive functions, trampolines, a pattern matching class, implement an language interpreter inside your factorial implementation, etc.
You assertion on how many ways one could screw up on OO is irrelevant and besides, you'd really need a good understanding of OO to even try implementing those. I didn't mean to use to imply any of those methods were bad. I have revised my post so people can't miss the point.
Now, addressing your concerns. All of the examples except for few particularly egregious ones seems reasonable to me.
I'm sure there's more that's as elegant if not more elegant ways to do the same thing. You could translate those to an OO language quite easily too, though with a lot more code. For example, what foldl does, from my understanding, is to recursively apply a function to both a starting value and the first element of a list, and then the same thing again, where the result from the last call becomes the starting value and the rest of the list becomes the list we're working on, up until the list is empty.
Not sure if I've explained this well but I've not been into this long. If you see the second function, that's what it's doing, except it's working just with numbers and not a list. Since foldl works on lists, you need an enumerator, which in this case is just 1 through to n. The sixth example is great here.
Product could easily be an abstraction on something quite similar to three, which itself could very well be an abstraction quite similar to two using lists, functions to deal with lists and a given function. Please correct me if I'm wrong at any point here. I'm looking to improve these skills quite a lot so it'd be very, very welcome. You got the core, I will correct your edges.
The [ And no foldl is not an abstraction of 2 because foldl is tail recursive while 2 is not. But you are right in that any tail recursive function on a list can be rewritten using foldl.
What is particular to haskell vs a strict language like say F is that foldl can still pop a stack due to haskell's laziness.
You want foldl' as it will force the initial argument.
Fold is also a fundamental operation on any algebraic data structure such as trees , lists, and natural numbers. You can write filter, map, filtermap etc in terms of it for example.
There is so much to say about fold - they are like the cupboard which leads to narnia.. Oh wow, thanks! I should have also noted I have no experience with the language in the examples so please ignore any ignorance there probably should have read up on it. Functional programs can be very terse and elegant.
This demonstrates the similarity in terms of higher-level concepts. This guide is also motivated by the release of Java 8 and its introduction of lambda expressions to the language.
Introduction ix. Having said all that. Design patterns are commonly used as a vocabulary of shared knowledge amongst object-oriented programmers. In Chapter 3. Over the next two chapters.
It has a fairly common idiom in which. This interface has a single method. Example There are still four lines of 2 Chapter 1: The anonymous inner class provides the implementation of this method. We shall also be looking at the syntax of lambda expressions in the Java programming language. Anonymous inner classes were designed to make it easier for Java programmers to represent and pass around behaviors.
In this example.
The Basics of Lambda Expressions We will define a lambda expression as a concise way of describing an anonymous function. Swing is a platform- agnostic Java library for writing graphical user interfaces GUIs.
As I mentioned in the Introduction. In Example Using an anonymous inner class to associate behavior with a button click button. The event listener can then perform some action in response to the user input see Example If we want a lambda The Basics of Lambda Expressions 3.
Using a lambda expression to associate behavior with a button click button. Another difference between this example and the anonymous inner class is how we declare the variable event. Method References A common idiom you may have noticed is the creation of a lambda expression that calls a method on its parameter. In Java 8. What is happening under the hood is that javac is inferring the type of the variable event from its context—here. Instead of passing in an object that implements an interface.
If you were to use a lambda expression to create an Artist. If we were to write the previous lambda expression using a method reference. You can also call constructors using the same abbreviated syntax.
Here is how you would create a String array: Another thing to notice here is that method references automatically support multiple parameters. Summary Well. Summary 5. This makes parameterizing code by behavior a lot more attractive. In fact. For example. This is key to functional programming.
Higher-order functions are just functions. This is the idea that we can pass behavior around and treat it like another value. An inevitable fact of software development is that requirements change over time. Whether because a new feature needs to be added. Each of the principles corresponds to a set of potential code smells that can exist in your code.
In the case of all these object-oriented principles. The goal here is to both show functional and object-oriented programming are related. The name itself is an acronym. Single responsibility. Many books have been written on this topic. Interface segregation. Liskov substitution.
If you tried to divide up a cohesive class. The single-responsibility principle is stronger than that. A class should not just have a single responsibility: Well lambda expressions make it a lot easier to implement the single-responsibility principle at the method level. This is part of the idea of a design exhibiting strong cohesion. This is a good motivation to decompose this problem at the high level into two classes: This possibly introduces bugs and also impedes the ability of the code base to evolve.
When the requirements of your software change. You might also wish to change the level of detail in the BalanceSheet itself. A class is cohesive if its methods and fields should be treated together because they are closely related.
If you have a class that has more than one responsibility. In other words. For the most part. As shown in Example If we want to count the number of primes for a very large upTo value. We can refactor our code to use the Java 8 streams library see Example Here we use the range method to count the numbers between 0 and upTo. One way of designing the MetricDataGraph class would be to have each of the new metric points pushed into it from the agent that gathers the data. We can also use higher-order functions and immutability to achieve similar aims in a functional style.
We can resolve this issue by introducing an abstraction. Now our MetricDataGraph API can be simplified to not depend upon the different types of metric that it needs to display. How can you extend the functionality of a class without having to change its implementation? The actual answer is that you rely on an abstraction and can plug in new functionality that fits into this abstraction. The ThreadLocal class provides a variable that is special in the sense that each thread has a single copy for it to interact with.
If we wanted to add. Higher-Order Functions Higher-order functions also exhibit the same property of being open for extension. Its static withInitial method is a higher-order function that takes a lambda expression that represents a factory for producing an initial value.
A good example of a class that proclaims its immutability but actually is only observably immutable is java. I mention immutable objects in the context of this report because they are a fairly familiar concept within functional programming. Observable immutability means that from the perspective of any other object.
We can also generate completely different behavior by passing in a different lambda expression. If we reflect on these different approaches.
Because our abstraction needs to be represented by an interface upon which methods are called. The higher-order function calls its single method. The Liskov substitution principle is often stated in these very formal terms. A final point that I think is worth reflecting on is that in the context of Java 8. Informally we can think 14 Chapter 2: Then q y should be true for objects y of type S where S is a subtype of T.
There is no internal state to mutate. Within a modern Agile developer environment.
Immutable objects are also of particular interest because they are inherently thread-safe. Functional programming tends to take a different perspective to LSP. We might define our worker interface as follows: Where parent always stuck left or maintained something.
We can split out that property into four distinct areas: The Interface-Segregation Principle The dependency of one class to another one should depend on the smallest possible interface In order to properly understand the interface-segregation principle. Where the parent worked. Where the parent caused an effect. This is shown in Example The relationship is explicit and based upon the name of the class.
As time passes. Now the interesting point about this example is that it all relates to subtyping. Our robots also do work in the factory. This applies equally to interfaces as well as classes. Parsing the headings out of a file class AssemblyWorker implements Worker class Manager implements Worker class Robot implements Worker When the compiler comes to check whether a parameter argument is type checked.
Most statically typed object-oriented languages. This means that for a class called Foo to extend a class called Bar. In our worked example. The alternative approach is called structural subtyping. The Dependency-Inversion Principle Abstractions should not depend on details. So if you call a method called getFoo on a variable. If we think about this hypothetical example in a language which uses structural subtyping. The duck typing in languages like Ruby and Python is a dynamically typed variant of structural subtyping.
A minimal interface is automatically inferred by the compiler from the use of these parameters. The Dependency-Inversion Principle The key is that the parameter worker has no explicit type. On of the ways in which we can make rigid and fragile programs that are resistant to change is by coupling high-level business logic and low-level code designed to glue modules together. Dependencies In this system. Figure This allows us to reuse the high-level code in a way that is abstract of the details upon which it depends.
Our application takes in a sequence of electronic business cards as input and accumulates our address book in some storage mechanism. This is because these are two different concerns that may change over time. In this case. This is the dependency- inversion principle at work. Our method is going to extract the headings from a file by reading the file.
We can therefore easily reuse them in another system. In order to give ourselves the flexibility to change these components within our system. In the context of lambda expressions. The implementation of our accumulation module depends upon these abstractions.
We can pass in the specific details of these implementations at runtime. The code looks like Example We can also change them. What we really want to do is write some code that finds the headings and delegates the details of a file to another method. A Stream is much safer and less open to abuse. This approach.
This exception then gets thrown in the event of a problem. To summarize. Summary The handler function represents the body of whatever code we want to use with this function.
We can easily use them with lambda expressions.
We talked about how the single-responsibility principle means that classes should only have a single reason to change. This is wrapped up in BufferedReader. Finally we talked about how higher-order functions were really a form of dependency inversion. The interface-segregation principle encourages us to minimize the dependency on large interfaces that have multiple responsibilities.