+ All Categories
Home > Documents > Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) &&...

Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) &&...

Date post: 22-Sep-2020
Category:
Upload: others
View: 1 times
Download: 0 times
Share this document with a friend
44
Kombinatoren
Transcript
Page 1: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Kombinatoren

Page 2: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Combinators

Page 3: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Gregor Trefs

32 years old

Team Lead @LivePerson

Organizer of @majug

Founder of SWK Rhein-Neckar

twitter/github: gtrefs

Page 4: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Who knows what

a function is?

a lambda expression is?

a combinator is?

Page 5: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

add :: Num a => a -> a -> aadd = \x -> \y -> x + y

Page 6: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

int addOne(int i){ return i+1; }

Page 7: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

Function<Integer, Integer> addOne;addOne = i -> i + 1;

Deduction Context

Lambda expression

Page 8: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

int compute(int i, Function<Int, Int> f)

Higher order

First order

Page 9: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

Function<Int, Int> makeAdder(int i){ return x -> x + i;}

Function<Int, Int> addOne = makeAdder(1)

Return value Factory

Page 10: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

Function<Int, Function<Int, Int>> f;

f = x -> (y -> x + y);

Function<Integer, Integer> addOne = f.apply(1);

Parameter of

first function

Returns function

Parameter of

returned function

Returns int

Page 11: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Recap: Functions

addOne = f.apply(1); addTwo = f.apply(2);

Function<Int, Int> addThree = x -> addTwo.apply(addOne.apply(x));

addFive = addThree.compose(addTwo);

Typesafe composition

Page 12: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

Combine primitives into more complex structures

Function Functions Functions

Page 13: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

Primitives are uncombined functions

Page 14: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

Combinators compose primitives and/or structures into more complex structures

Page 15: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

Combinators are functions that build other functions

Page 16: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

User Validation

Function<User, Boolean>

Page 17: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

@Testpublic void yield_valid_for_user_with_email_and_non_empty_name(){ User gregor = new User("Gregor Trefs", 32, "[email protected]");

UserValidation validation = todo();

assertThat(validation.apply(gregor), is(true));}

interface UserValidation extends Function<User, Boolean> {}

Page 18: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

@Testpublic void yield_valid_for_user_with_email_and_non_empty_name(){ User gregor = new User("Gregor Trefs", 32, "[email protected]"); UserValidation nameIsNotEmpty = user -> !user.name.trim().isEmpty(); UserValidation mailContainsAtSign = user -> user.email.contains("@"); UserValidation validation; validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user);

assertThat(validation.apply(gregor), is(true));}

interface UserValidation extends Function<User, Boolean> {}

Page 19: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Primitives and Combinators

@Testpublic void yield_valid_for_user_with_email_and_non_empty_name(){ final User gregor = new User("Gregor Trefs", 32, "[email protected]"); final UserValidation validation = nameIsNotEmpty.and(mailContainsAtSign);

assertThat(validation.apply(gregor), is(true));}

public interface UserValidation extends Function<User, Boolean> { UserValidation nameIsNotEmpty = user -> !user.name.trim().isEmpty(); UserValidation mailContainsAtSign = user -> user.email.contains("@"); default UserValidation and(UserValidation other){ return user -> this.apply(user) && other.apply(user); }

default UserValidation or(UserValidation other){ return user -> this.apply(user) || other.apply(user); }}

Page 20: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Embedded domain specific language: Primitives and combinators are from the

validation domain

Page 21: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Separation of validation description and execution

Page 22: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Validation has no shared mutable state

Page 23: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Boolean is bad for representing validation results

Page 24: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Hard to determine which rules invalidated the result

Page 25: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Semantic is implicit and context specific

Page 26: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

Type for representing the validation result is needed

Page 27: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

@Testpublic void yield_invalid_for_user_without_email(){ User gregor = new User("Gregor Trefs", 32, ""); ValidationResult result = nameIsNotEmpty.and(eMailContainsAtSign).apply(gregor); assertThat(result.getReason().get(), is("E-Mail is not valid."));}

public interface UserValidation extends Function<User, ValidationResult> { UserValidation nameIsNotEmpty = todo(); UserValidation eMailContainsAtSign = todo(); default UserValidation and(UserValidation other){ return todo(); }}

Page 28: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Return value reasoning

@Testpublic void yield_invalid_for_user_without_email(){ User gregor = new User("Gregor Trefs", 31, ""); ValidationResult result = nameIsNotEmpty.and(eMailContainsAtSign).apply(gregor); assertThat(result.getReason().get(), is("E-Mail is not valid."));}public interface UserValidation extends Function<User, ValidationResult> { UserValidation nameIsNotEmpty = user -> !user.name.trim().isEmpty()?valid():invalid("User name is empty"); UserValidation eMailContainsAtSign = user -> user.email.contains("@")?valid():invalid("E-Mail is not valid."); default UserValidation and(UserValidation other){ return user -> { ValidationResult result = this.apply(user); return result.isValid() ? other.apply(user) : result; }; }}

Page 29: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Domain specific approach

Page 30: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Implicit information is modelled explicit

Page 31: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Separation of concerns in primitives and composability with combinators

Page 32: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Extensibility by using the context

UserValidation ext; ext = nameIsNotEmpty.and(u -> …)

Page 33: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Reusability: Once described, a function can be applied manyfold

Page 34: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

Does my team understand the concepts of functional programming?

Page 35: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Benefits and Disadvantages

How to determine primitives and combinators?

Page 36: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

Design your API with Combinators

Comparator

Page 37: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

@Testpublic void sort_users_by_age_and_name(){ final User gregor = new User("Gregor Trefs", 32, "[email protected]"); final User petra = new User("Petra Kopfler", 30, "[email protected]"); final User robert = new User("Robert Schmidt", 32, "[email protected]");

Comparator<User> byAge = Comparator.comparing(user -> user.age); Comparator<User> byName = Comparator.comparing(user -> user.name); Comparator<User> byAgeThenName = byAge.thenComparing(byName);

assertThat(byAgeThenName.compare(gregor, petra), is(1)); assertThat(byAgeThenName.compare(gregor, robert), is(-11));}

Page 38: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

JSON Encoding and Decoding

Function<JValue, DecodeResult<T>>Function<T, JValue>

Page 39: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

@Testpublic void user_json_codec(){ var user = new User("Gregor Trefs",32,"[email protected]"); EncodeJson<User> encode = Encoders.encode( FieldEncoder.EString("name"), FieldEncoder.EInt("age"), FieldEncoder.EString("email")) .contramap(u -> Tuples.of(u.name, u.age, u.email)); DecodeJson<User> decode = Decoders.decode( FieldDecoder.TString("name"), FieldDecoder.TInt("age"), FieldDecoder.TString("email"), User::new);

var codec = JsonCodec.lift(decode, encode); var encodedThenDecoded = codec.fromJson(codec.toJson(user)).unsafeGet();

assertThat(encodedThenDecoded.email, is(user.email)); assertThat(encodedThenDecoded.age, is(user.age)); assertThat(encodedThenDecoded.name, is(user.name));}

Page 40: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

To build functions

Page 41: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

When to use it

FizzBuzz: Number -> Word

Validation: User -> ValidationResult

Parsing: String -> AST

Projection: EventStream -> Aggregate

Encoding: Object -> JSON

Decoding: JSON -> T

Page 42: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Local type inference: Impossible types

@Testpublic void use_combinators_from_different_namespaces(){ var gregor = new User("Gregor Trefs", 32, "[email protected]"); var validations = (NameValidations & AgeValidations) UserValidation::create;

var validator = validations.olderThan(20).and(validations.nameIsUpperCase()); var validationResult = validator.apply(gregor);

assertThat(validationResult.getReason().get(), containsString("upper case."));}public interface NameValidations extends UserValidation { default UserValidation nameIsUpperCase(){ … }}public interface AgeValidations extends UserValidation { default UserValidation olderThan(int age){ … }}public interface UserValidation extends Function<User, ValidationResult> { static ValidationResult create(User user){ throw new UnsupportedOperationException(); }}

Page 43: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

Contact

@gtrefs

[email protected]

linkedin.com/in/gregor-trefs

The EndQuestions?

Page 44: Kombinatoren - andrena · validation = user -> nameIsNotEmpty.apply(user) && mailContainsAtSign.apply(user); ... functional programming? Benefits and Disadvantages How to determine

● My blog post about the topic

http://bitly.com/2lryGxJ

● Functional Programming in

Scala (The red one)

Java (The blue one)

● Background picture by

John Salzarulo

Literatureand links


Recommended