Commit 53c557f9 authored by Reimers, Dominik's avatar Reimers, Dominik
Browse files

Reduced to core functionality and added CI

parent 5514ee0b
deploy:
image: maven:3.6-jdk-11
script:
- 'mvn deploy -s ci_settings.xml'
only:
- main
\ No newline at end of file
4b5ec429556e123b05cac5efccc73722b5ac04cd
\ No newline at end of file
language: java
sudo: required
jdk:
- oraclejdk8
install: true
script:
- sudo apt-get update && sudo apt-get install oracle-java8-installer
- java -version
- ./fpinjava-parent/gradlew
notifications:
email:
recipients:
- build@fpinjava.com
on_success: always # default: change [always|never|change]
on_failure: always # default: always [always|never|change]
\ No newline at end of file
## General questions
### Question: You haven’t defined xyz. What does it mean?
Always read the complete explanation before trying to answer the question. Many subjects imply cyclic dependencies between their parts. This kind of explanation must be read at least twice. Take the following example:
public class Case<T> {
public static <T> Case<T> mcase(Supplier<Boolean> condition, Supplier<Result<T>> value) {
return new Case<>(condition, value);
}
public static <T> DefaultCase<T> mcase(Supplier<Result<T>> value) {
return new DefaultCase<>(() -> true, value);
}
You may wonder what this DefaultCase class is that we haven’t yet defined. But the DefaultCase definition is as follows:
private static class DefaultCase<T> extends Case<T> {
...
If we had defined `DefaultCase` first, you might have wondered what `Case` was. Reading the whole listing give all clues:
public class Case<T> extends Tuple<Supplier<Boolean>, Supplier<Result<T>>> {
private Case(Supplier<Boolean> booleanSupplier, Supplier<Result<T>> resultSupplier) {
super(booleanSupplier, resultSupplier);
}
public static <T> Case<T> mcase(Supplier<Boolean> condition, Supplier<Result<T>> value) {
return new Case<>(condition, value);
}
public static <T> DefaultCase<T> mcase(Supplier<Result<T>> value) {
return new DefaultCase<>(() -> true, value);
}
private static class DefaultCase<T> extends Case<T> {
private DefaultCase(Supplier<Boolean> booleanSupplier,
Supplier<Result<T>> resultSupplier) {
super(booleanSupplier, resultSupplier);
}
}
@SafeVarargs
public static <T> Result<T> match(DefaultCase<T> defaultCase, Case<T>... matchers) {
for (Case<T> aCase : matchers) {
if (aCase._1.get()) return aCase._2.get();
}
return defaultCase._2.get();
}
}
But because we might not always put the whole code in one part of the book, you should always refer to the accompanying code.
## Lambdas
### Question: How can you instantiate the Xyz interface without a concrete implementation?
For example, given the following interface:
public interface Effect<T> {
void apply(T t);
}
How can you instantiate it in the following?
Effect<String> success = s -> System.out.println("Mail sent to " + s);
This is a lambda (new in Java 8) that is roughly equivalent to the following:
static Effect<String> success = new Effect<String>() {
@Override
public void apply(String s) {
System.out.println("Mail sent to " + s);
}
};
This is described in chapter 2, along with the step-by-step process of converting the anonymous class notation to the lambda notation.
The two forms are equivalent only from the programmer’s point of view. Differences exist in the way the two forms are handled by the compiler.
### Question: In the following code, how can the compiler know that the lambda is to be compiled into a Supplier<...> (or whatever)?
public static <T> DefaultCase<T> mcase(Supplier<Result<T>> value) {
return new DefaultCase<>(() -> true, value);
}
The lambda `() -> true` is to be compiled into an anonymous class implementing a functional interface defining a single method taking no parameter (this is indicated by `()`) and returning a `boolean`.
Which interface to choose is indicated by the `DefaultCase` constructor:
private DefaultCase(Supplier<Boolean> booleanSupplier, Supplier<Result<T>> resultSupplier) {
super(booleanSupplier, resultSupplier);
}
The first parameter has to be a `Supplier<Boolean>`.
## Method references
Method references are syntactic sugar over lambdas. When the right part of a lambda is a call to a method that takes the left part as its argument, such as this:
map(x -> change(x))
flatMap(x -> a.change(x))
It can be replaced with a method reference:
map(this::change)
flatMap(a::change)
If the method is static, just replace this with the name of the class:
forEach(x -> System.out.println(x))
This may be replaced with the following:
forEach(System.out::println)
This works also for constructors:
map(x -> new Person(x))
This may be replaced with the following:
map(Person::new)
## Type annotations
All Java programmers know how to use class-level type annotations. This is the simplest use of generics. Class-level type annotations come in two categories.
### Class definition
A class can be defined with a type annotation. For example, a `Comparator` for strings will be defined as follows:
public class String Comparator implements Comparator<String> {
...
}
### Class instantiation
When instantiating a class, we can use a type annotation such as this:
List<String> list = new ArrayList<String>();
We can even use the diamond syntax (since Java 7) to simplify code typing:
List<String> list = new ArrayList<>();
We don't need to repeat the type annotation on the right side because the compiler is able to infer it from the left side.
### Parameterized types
We can define `List` and `Comparator` in such ways because these classes are defined with type parameters:
public class List<T> {
...
}
public class Comparator<T> {
...
}
Here, `T` is a type variable. Using such variables make the classes generic. One definition of `List` can be used for all lists of different types by replacing `T` with the real type.
All this is well known. Methods and fields of such classes can be written using these types. For example, the `Pair<T, U>` class can be defined as follows:
public class Pair<T, U> {
private final T left;
private final U right;
public Pair(T t, U u) {
left = t;
right = u;
}
public T getLeft() {
return left;
}
public U getRight() {
return right;
}
}
You can see that the two fields (left and right), the constructor, and the two getters use the `T` type parameter.
### Method declaration type annotations
Methods must sometimes be parameterized with type parameters that aren’t in scope, which means that they weren’t declared with the class. This is most often the case for static methods, because the class type parameters aren’t available to such methods.
Static method declaration can be type annotated. For example, to define a static factory method for the `Pair` class, we can write the following:
public static <T, U> Pair<T, U> create(T t, U u) {
return new Pair<>(t, u);
}
Note the use of the diamond syntax, which is possible because Java is able to infer the type of `Pair` to create by looking at the method signature.
Instance methods can also be annotated. For example, we could define a method `mapLeft` to transform the left value of the pair by applying a function to it:
public <V> Pair<V, U> mapLeft(Function<T, V> f) {
return new Pair<>(f.apply(left), right);
}
### Method invocation type annotations
Type inference is mostly used by Java while dereferencing methods and fields. Sometimes Java isn’t able to infer the correct type, whether because it’s not possible (not enough information) or because Java type inference is weak (not enough information for the Java compiler). For example, a functional `List` class can define a method returning an empty list, such as `List.list()`. An empty list generally has a raw type, which means it’s not type annotated. The same empty list can be used for an empty list of `String` elements or an empty list of `Integer` elements. But to use it, we must give it a type. Java might be able to infer the type, as in the following:
List<String> list = List.list();
In some cases, however, Java can’t infer the type, as in this code:
public static <A> Function<A, Function<List<A>, A>> headOr() {
return foldRight().apply(constant());
}
Here, Java can’t infer the type when calling the instance method `foldRight`. This example doesn’t compile, and the error message is as follows:
Error:(113, 27) java: incompatible types: funclib.Function<java.lang.Object,funclib.Function<exercises.List<java.lang.Object>,java.lang.Object>> cannot be converted to funclib.Function<A,funclib.Function<exercises.List<A>,A>>
Java can't infer the type and is using `Object`. To fix this, we can annotate the method call:
public static <A> Function<A, Function<List<A>, A>> headOr() {
return List.<A, A>foldRight().apply(constant());
}
The method type annotations are to be written after the dot, so we must add the class name (or another reference for instance methods) in order to be able to use the dot. The following won’t compile:
public static <A> Function<A, Function<List<A>, A>> headOr() {
return <A, A>foldRight().apply(constant());
}
### Field type annotations
In imperative Java, it’s rarely (if ever?) useful to annotateion fields with a type. And by the way, field type annotations don’t exist in Java. In functional Java, we sometimes want to declare functions as static properties. So, if the functions must use type annotations, we cannot use fields to represent these properties. We must use methods instead (see below).
### What are properties?
As indicated in the Java documentation, a property is a characteristic of an object that can be read and/or written. In imperative Java, properties can often be written and read, so they’re generally implemented as private fields with public getters and setters.
In functional Java, properties are immutable, so we don’t need setters. Properties can be implemented as public final fields or private final fields with getters. Properties are generally initialized either in the constructor, or at declaration time with a literal value. In the later case, we’ll speak about a literal property, or a literal constant, because the property is immutable.
To define a function as a literal constant, we’d like to write the following:
public static <T, U> Function<T, U> function = x -> …;
But we can’t do this because type-annotating fields is forbidden. The solution is to implement the constant as a method returning a literal value (defined in the body of the method):
public static <T, U> Function<T, U> function() {
return x -> …;
}
Here, `function()` isn’t the function. It’s a method returning the function. To use the function, instead of writing:
T argument = ...
U result = function.apply(argument);
we’ll write
T argument = ...
U result = function().apply(argument);
This repository contains code, exercises and solutions from the book "Functional Programming in Java". Along with the book itself, it's the
closest you'll get to having your own private functional programming tutor without actually having one.
[![Build Status][badge-travis]][travis]
[badge-travis]: https://travis-ci.org/fpinjava/fpinjava.png?branch=master
[travis]: https://travis-ci.org/fpinjava/fpinjava
## How to use the project
The code is available as a Gradle project that may be used by:
- Running Gradle commands in a terminal
- Importing in Eclipse (with limitations)
- Importing in IntelliJ
- Importing in NetBeans
One may of course use only the source files to import them into any other editor and/or compile and run them from the command line. Running Gradle
commands will not be described here. If you choose this way, you should already know how to do it.
## Downloading the project
There are two ways to download this project. You may:
- Click on the **Download ZIP** button on this page (https://github.com/fpinjava/fpinjava) to download a ZIP archive, then unzip it in the directory
of your choice
- Or you can clone the project using Git, in order to be able to update it easily when modifications or additions are made to the project. For this,
you will need to have Git installed on your PC and use the URL available on this page in the **SSH clone URL** area.
## Importing into Eclipse
Note: Eclipse is not fully compatible with Java. Eclipse does not use the Oracle compiler. The compiler used by Eclipse is not fully compatible with the Oracle Java 8 compiler. This might change in the future, but at this time, not all chapter examples will run under Eclipse. You can use Eclipse Luna to edit and run the code through chapter 8. You can't use Eclipse Mars. We have no information about if and when this will be fixed.
To import the project into Eclipse, you must first install the Gradle plug-in. To do this:
1. Select **Help > Eclipse** MarketPlace
1. In the **Find** text area, type **Gradle**
1. Click the **Go** button
1. Select the **Gradle integration for Eclipse** plugin and click the corresponding **Install** button.
You will have to accept installing unverified software, and then restarting Eclipse.
### Importing the project
You now need to import the `fpinjava-parent` project into Eclipse:
1. Select **File > Import > Gradle > Gradle Project**
1. In the **Import Gradle** project dialog box, click the **Browse** button, navigate to the directory were you put the project and select
the `fpinjava-parent` directory
1. Click on the **Build Model** button
1. In the dialog box, verify that all modules are selected. If they are not, selecting the parent module will automatically select all sub-modules.
1. Keep all standard settings untouched and click **Finish**. The project should now be imported.
## Importing into IntelliJ
To import the project into intelliJ:
1. Select **File > Import** project
1. In the **Select file or directory to import** dialog box, navigate to the directory where you have installed the project and select
the `fpinjava-parent` directory.
1. In the import project dialog box, select Gradle and click the **Next** button.
1. Click the **Finish** button
1. Find the `fpinjava-parent` project in the **Project** pane, right-click the project and select **Open module settings**.
1. In the **Project Structure** dialog box, click on **Project** in the **Project setting** pane and select **8 - Lambdas, type annotations etc.**
for the **Project language level** option.
1. Click **OK**.
## Importing into NetBeans
To import the project into Netbeans, you must first install the Gradle plug-in. To do this:
1. Select **Tools > Plugins > Available Plugins**
1. In the **Search** text area, type **gradle**
1. Tick the **Install** checkbox on the **Gradle Support** row.
1. Click the **Install** button.
You will have to accept installing unsigned software, and then restarting NetBeans.
### Importing the project
You now need to import the `fpinjava-parent` project into NetBeans:
1. Select **File > Open Project...**
1. In the **Open Project** dialog box, go to the directory were you put the project and select
the `fpinjava-parent` project name
1. Click on the **Open Project** button
The project should now be imported.
## Doing the exercises
For each chapter, you will find two modules called `chaptername-exercises` and `chaptername-solutions` . Go to the first exercise in
the `src/main/java` hierarchy. Here, you will find some code with either a comment saying "To be implemented" or method(s) with the
implementation replaced with a single line throwing a runtime exception. Just implement the missing code.
Note that code is often duplicated from one exercise to the another, so you should not look at the code for exercise 2 before doing exercise 1,
since exercise 2 will often contain the solution to exercise one.
## Verifying your answers
To verify that your solution is working, go to the corresponding unit test, in the `src/test/java` hierarchy of the same module. Right-click
on the test class (which has the same name as the exercise class with the addition of the `Test` suffix) and select **Run as JUnit test**. The test should
succeed. If it doesn't, fix your code and try again.
## Looking at solutions
If you don't find the correct solution to an exercise, you can look at the corresponding `chaptername-solutions` module. You may run the solution
test to verify that the solution is working.
## Remarks
Lots of code is duplicated. This is done so that all exercises are made as independent as possible. However, code reused from previous chapters
is copied to the `fpinjava-common` module and should be used from there.
## Module names
Code modules are generally named after the chapter titles, and not the chapter numbers, which sometimes make them difficult to find. Here is the list of the modules:
* Chapter 1: fpinjava-introduction
* Chapter 2: fpinjava-usingfunctions
* Chapter 3: fpinjava-makingjavafunctional
* Chapter 4: fpinjava-recursion
* Chapter 5: fpinjava-lists
* Chapter 6: fpinjava-optionaldata
* Chapter 7: fpinjava-handlingerrors
* Chapter 8: fpinjava-advancedlisthandling
* Chapter 9: fpinjava-laziness
* Chapter 10: fpinjava-trees
* Chapter 11: fpinjava-advancedtrees
* Chapter 12: fpinjava-state
* Chapter 13: fpinjava-io
* Chapter 14: fpinjava-actors
* Chapter 15: fpinjava-applications
Most modules exist in two versions: exercises and solutions. However, chapters 1 (fpinjava-introduction), 14 (fpinjava-actors) and 15 (fpinjava-applications) have no exercises.
Most modules have unit tests that may be run to verify that your solutions to exercises are correct. However, chapter 13 has not unit tests. Instead, all packages in this chapter have executable programs that may be run to verify that the output corresponds to what is expected.
closest you'll get to having your own private functional programming tutor without actually having one.
\ No newline at end of file
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
<servers>
<server>
<id>gitlab-maven</id>
<configuration>
<httpHeaders>
<property>
<name>Job-Token</name>
<value>${env.CI_JOB_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
</settings>
\ No newline at end of file
version = '1.0'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
allprojects {
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
subprojects {
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
repositories {
mavenCentral()
}
}
project(':fpinjava-common') {
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-introduction') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-usingfunctions-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-usingfunctions-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-makingjavafunctional-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-makingjavafunctional-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-recursion-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.11'
}
}
project(':fpinjava-recursion-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.11'
}
}
project(':fpinjava-lists-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-lists-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-optionaldata-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-optionaldata-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-handlingerrors-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-handlingerrors-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-advancedlisthandling-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-advancedlisthandling-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-laziness-exercises') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}
}
project(':fpinjava-laziness-solutions') {
dependencies {
compile project(':fpinjava-common')
testCompile group: 'junit', name: 'junit', version: '4.+'
}