Relevancy of
Functional programming
paradigms in Real World
Sathish Kumar
Flipkart Ads Team
Outline
- Imperative vs Functional programming (FP)
- Functional programming (FP) in Software Industry
- Functional programming (FP) in Flipkart Ads systems
- Ads Problem statements
- Functional solutions
- Java 8 vs Haskell
- Abstractions - Lambdas, Functors, Monads
Imperative/OO programming

Double calculateTotalCashback(Order order){
Double totalCashback = 0.0;
for (OrderItem orderItem : order.getOrderItems()) {
if(orderItem.hasOffer() &&
orderItem.getOffer().getCashback() > 0.0) {
totalCashback += orderItem.getOffer().getCashback();
}
}
return totalCashback;
}Mutable State
Reasoning about code - How vs What
Lock based concurrency
Functional programming
Quick Sort in Haskell
qsort [] = []
qsort (x:xs) = qsort small ++ [x] ++ qsort large
where small = [y | y <- xs, y <= x]
large = [y | y <- xs, y > x]No side effects (or) state changes
Type safety and Type inference
Immutable data - Multicore concurrency
Pivot
Larger than Pivot
Smaller than Pivot
Functional programming
Infinite Fibonacci in Lisp
Lazy evaluation
Functions are composable
(define (fib a b)
(cons-stream a (fib b (+ a b))))
(take 10 (filter isEven fib))
Infinite recursion
Higher order functions

FP in Academia - 1990
Why functional programming matters - John Hughes
FP in Industry

2010
2011
Twitter uses Scala for backend services
FP in Industry
2012
Haskell at Standard Chartered
2014
Ocaml at Jane Street
Wall street's secret sauce
FP in Industry
2014
WhatsApp: Scaling to Billions of messages in Erlang

FP in Industry
2015
Facebook: Fighting Spam with Haskell
FP in Industry
- Lambdas
- First class functions
- Higher order functions
- Streams
- Optional
- Function composition
2014
Java 8: Functional features
FP in Industry
Functional style
No matter what language you work in, programming in a functional style provides benefits. You should do it whenever it is convenient, and you should think hard about the decision when it isn't convenient"
- John Carmack (Creator of Quake, Doom, Wolf)

Flow of ideas
Imperative / OOP
Functional
Math
Java
Python/Ruby
Javascript
Scala
Lisp
Haskell
Lambda Calculus
Category Theory
A language that doesn't affect the way you think about programming, is not worth knowing."
- Alan J. Perlis
Ads Serving
App
Desktop
Exchange



Ads Serving Stack








| Campaign | Impressions | Clicks |
|---|---|---|
| 101 | 100,000 | 1000 |
| 102 | 200,000 | 2000 |
Ad Selection flow

Data Model

Conventions
Functional concept
# foo
foo :: String -> String
foo x = "Hello " ++ xpublic String foo(String name){
return "Hello " + name;
}Haskell code example
Java code example

Data Model
# Type
# Type alias
type Price = Double
type CTR = Double
data PriceType = CPM | CPV | CPC
type Id = Int
type Score = Double
data Advertiser = Advertiser Id
data Campaign = Campaign Id Advertiser PriceType Price
data Banner = Banner Id Campaigndata Slot = Slot Id
data User = UnknownUser | User Id
data SlotGroup = SlotGroup Id [Slot]
Campaign, Banner attrs
Advertiser, Campaign, Banner
Slot, User
constructors

Relevance - Campaigns










1. Active
2. Gender targeting

Relevance - Campaigns
relevantCampaigns :: Context -> Map[Slot, [Campaign]]
1. Active
2. Gender targeting
isActive = \campaign -> isActive campaignisGenderTargeted = \campaign, context ->
case (getGender campaign) of
Nothing -> True
Just (gender) -> gender == getGender (getUser context)# Type signature
# Lambdas
Predicate<Campaign> isActive = campaign -> campaign.isActive();
Predicate<Campaign> isGenderTargeted(Context context) {
return campaign -> context.getUser()
.map(gender -> gender.equals(campaign.getGender()))
.orElse(true);
}# Predicate

Relevance - Campaigns
List<Campaign> getRelevantCampaigns(Context context){
return campaignStore.getAll().stream()
.filter(isActive())
.filter(isGenderTargeted(context))
.collect(toList());
}Combining predicates - AND
# Streams
# Filter

Relevance - Campaigns
Combining predicates - AND
# Function
Predicate<Campaign> getRelevanceFilters(Context context){
return isActive()
.and(isGenderTargeted(context))
.and(isFrequencyCapped(context));
}
Adding a new relevance rule is
as simple as adding a predicate
composition

Relevance - Banners
1. Matches template
2. Matches width and height





Relevance - Banners
relevantBanners :: Map[Slot, [Campaign]] -> Map[Slot, [Banner]]
1. Matches template
Predicate<Banner> forTemplate(Context context){
return banner -> banner.getTemplate().equals(context.getTemplate());
}2. Matches width and height
Predicate<Banner> forDimension(Context context){
return banner -> banner.getWidth().equals(context.getWidth()) &&
banner.getHeight().equals(context.getHeight());
}Predicate<Banner> getFilters(Context context){
return forTemplate(context)
.or(forDimension(context));
}
List<Banner> getRelevantBanners(Context context, List<Campaign> campaigns){
return bannerStore.getAll(campaigns).stream()
.filter(getFilters(context))
.collect(toList());
}
Relevance - Banners
Combining predicates - OR
[1,2,3] ++ [] == [1,2,3]
[1,2] ++ ([3,4] ++ [5,6]) == ([1,2] ++ [3,4]) ++ [5,6]
[] ++ [1,2,3] == [1,2,3]
List
Sum
5 + 0 == 5
(1 + 2) + 3 == 1 + (2 + 3)
0 + 5 == 5
Product
5 * 1 == 5
(1 * 2) * 3 == 1 * (2 * 3)
1 * 5 == 5
False && True == False
(True && True) && False == True && (True && False)
True && False == False
Boolean AND
True || False == True
(True || True) || False == True || (True || False)
False || True == True
Boolean OR
All of these are binary operations with Identity and Associativity
Monoid is an abstraction of this common pattern
Detour: Monoid
# Monoid

Detour: Monoid
Any
mconcat [Any False, Any False] == Any False
mconcat [Any False, Any True] == Any TrueAll
mconcat [All True, All False] == All False
mconcat [All True, All True] == All TrueMonoid: if you define identity and append, you get concat for free
isRelevant campaign = mconcat (map (\f -> f campaign) filters)
isRelevant banner = mconcat (map (\f -> f banner) filters)With these abstractions, Relevance simplifies to:
Ranking
1. Score banners by criteria
2. Sort banners by score
1. Samsung (score: 0.85)
2. Sony (score: 0.75)
3. BPL (score: 0.65)




Ranking
rankBanners :: Map[Slot, [Banner]] -> Map[Slot, [BannerScore]]
1. Score banners by criteria
2. Sort banners by score

What is the common pattern here?
relevantBanners :: Map[Slot, [Campaign]] -> Map[Slot, [Banner]]
rankBanners :: Map[Slot, [Banner]] -> Map[Slot, [BannerScore]]
Map<K, B> mapValues(Map<K, A> input, Function<A, B> transformer) {
return input.keySet().stream()
.collect(toMap(Function.identity(), transformer));
}
# Higher order
Ranking
functions

Ranking
mapValues(relevantBanners, bannerScorer())
public Map<Slot, List<BannerScore>> rankBanners(Map[Slot, List[Banner]] relevantBanners){
return mapValues(relevantBanners, scorer());
}
Function<Slot, List<BannerScore>> bannerScorer(){
return slot -> {
List<Price> prices = getPrices(banners);
List<CTR> ctrs = getCTRs(banners);
return StreamUtils.zip(
prices.stream(),
ctrs.stream(),
(price, ctr) -> calculate(price, ctr))
.collect(toList());
}
}
mapValues takes function as argument - Higher order function
bannerScorer returns function as output - First class function
# First class
functions
# zip

CTR Prediction Model
1. CTR: Click Through Rate
2. Logistic Regression Model
3. Input: Supply, Demand, User features
4. Output: Predicted CTR
n features
θi - Model coefficients
Xi - Boolean value of features
CTR Prediction Model
Features
1. Slot : slot:123 (0.001) or slot:default (-0.005)
2. Campaign : campaign:123 (0.002) or campaign:default (-0.005)
3. Gender : gender:male (0.003) or gender:female (0.003) or gender:default (-0.005)
4. Stores : store:books (0.004), store:mobiles (0.005), store:default (-0.005)
z = - 5.0 + 0.001 + 0.002 + 0.003 - 0.005
pCTR = 0.7%
CTR Prediction Model
# Optional
# Null safety
Feature genderFeature = context.getUser().getGender()
.map(value -> new Feature("gender:" + value))
.orElse(new Feature("gender:default"))Single valued features
Optional [Gender]
1. user.gender is present - Just [Gender] - map
2. user.gender is null - Nothing - orElse



Detour: Functors

Functors apply a function to a value in a box
*

# Functors
value -> new Feature("gender:" + value)
Gender
Feature
getGender() :: Maybe[Gender]
Optional[Gender]
"gender:female"
Feature
map

Java 8 Optional is a functor

Feature genderFeature = context.getUser()
.flatMap(user -> user.getGender())
.map(value -> new Feature("gender:" + value))
.orElse(new Feature("gender:default"))1. Optional [User]
user is present: flatMap
user is null: orElse
2. Optional [Gender]
user.gender is present: map
user.gender is null: orElse
# flatMap
CTR Prediction Model

# Monads

*
Monads apply a value in a box to a function that returns a value in a box
Detour : Maybe Monad
half()
map on Optional
flatMap on Optional
value -> new Feature("gender:" + value)
Gender
Feature
getGender() :: Maybe[Gender]
Optional[Gender]
"gender:female"
Feature
map
user -> user.getGender()
User
Optional[Gender]
getUser() :: Maybe[User]
Optional[User]
Optional[Gender]
flatMap
Java 8 Optional is a monad

Deduplication
1. Serve unique ads across multiple slots
2. Uniqueness can be by advertiser or campaign or banner









Dedup options:
1. None 2. By Advertiser 3. By Campaign 4. By Banner
dedupCriteria = Nothing
dedupCriteria = Just (\banner -> (advertiser (campaign banner)))
dedupCriteria = Just (\banner -> (campaign banner))
dedupCriteria = Just (\banner -> banner)dedupBanners :: Map[Slot, [BannerScore]] -> Map[Slot, Maybe[Banner]]
Deduplication
Workflow
# Function composition
Workflow is just function composition
compose :: (b -> c) -> (a -> b) -> a -> crelevantCampaigns :: Context -> [Campaign]
relevantBanners :: [Campaign] -> [Banner]
rankBanners :: [Banner] -> [BannerScore]
dedupBanners :: [BannerScore] -> Maybe[Banner]selectAds :: Context -> Maybe[Banner]
selectAds = dedupBanners . rankBanners . relevantBanners . relevantCampaigns


Summary
- Immutability
- Elegant and Expressive
- Reusability - Lambdas, Higher order functions
- Null safe code using Optional
- Functors, Monads - Function application patterns
- Functional languages change the way you think about programming
Questions?
Relevance of functional programming paradigms in real world (large code)
By sathish316
Relevance of functional programming paradigms in real world (large code)
Relevance of functional programming paradigms in real world
- 270