4

I was reading the-neophytes-guide-to-scala-part-10 where I came across following code.

type EmailFilter = Email => Boolean

val minimumSize: Int => EmailFilter = n => email => email.text.size >= n

I understood the first line where type alias EmailFilter is created for a function which takes email return boolean. But I don't understand the second line where we take email and number as input and returns boolean by checking size. Please decode the second line and explain me this syntactic sugar code for the function.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
Kumar Waghmode
  • 509
  • 2
  • 18

2 Answers2

14

There is no syntactic sugar whatsoever, just raw lambda expressions. If you plug in the type EmailFilter definition into the type in the second line, you obtain

Int => (Email => Boolean)

which is the same (because of right-associativity of =>) as

Int => Email => Boolean 

and this corresponds perfectly with

n   => email => (email.text.size >= n)

which essentially just says: given a number n, create an email filter that, given an email returns true if and only if the length of the email is at least n, so that, for example

minimumSize(100)

returns a closure that behaves just like

email => email.text.size >= 100

i.e. it filters all emails with length greater than or equal 100. Thus, with suitably defined example mails shortMail and longMail, you would obtain:

minimumSize(100)(shortMail) // false
minimumSize(100)(longMail) // true
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
5

The minimumSize function is a curried function.

Currying is a way to split a function call into multiple and sequential subfunction calls.

There are some many good advantages to curry function, one is that it allows your function to be more composable, by deferring the real data source.

Let's depict the usage of:

n => email => email.text.size >= n   

We can first call this function by passing a parameter for n only:

minimumSize(2) // partially applies the minimumSize function with 2 as n 

You will get at this time:

val nextFunction = email => email.text.size >= 2

Next you call nextFunction with an email:

nextFunction(Email("anemail@domain.com"))

You will get at this time a boolean:

val bool = Email("anemail@domain.com").text.size >= 2

So if we sum up:

We started with an Int, then an Email, then a Boolean:

Int => Email => Boolean

And by looking at this signature more carefully, you will recognize the EmailFilter signature.
Let's substitute:

Int => EmailFilter 

The idea is to make the EmailFilter acts as a template, that you can parameterize with some higher functions.
Here we parameterized the email text size comparison so that we can keep the EmailFilter generic.

Keep in mind that functional programming is all about composing functions.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
Mik378
  • 21,881
  • 15
  • 82
  • 180
  • 1
    What is `'anemail@domain.com'`? A Python string literal? Also, `String`s don't have member `.text`. – Andrey Tyukin May 17 '18 at 14:21
  • But `String` still has no member `.text`, and type `Email` is not the same as type `String` for all meaningful definitions of the type `Email`. Couldn't you introduce a `case class Email(text: String)` to make the whole example compilable? – Andrey Tyukin May 17 '18 at 14:23
  • It's a conceptual substitution (like pseudo-code), the goal is not to make compiler happy but to explain simply the idea. – Mik378 May 17 '18 at 14:24
  • 1
    Ok, then maybe, for the sake of conceptual simplicity, one could simply delete the `.text` from `email.text.size`, and typedef `type Email = String`. – Andrey Tyukin May 17 '18 at 14:25
  • 1
    haha was just about replacing `String` with `Email` wording (I forgot one occurrence), but you were faster :) – Mik378 May 17 '18 at 14:31
  • 1
    Mik378 Yeah, thanks, clearer now. If I really wanted to nitpick, I'd say that "anemail@domain.com" looks more like an *address* rather than *text*, but ok, it's obviously extendable to compilable code now, (+1) ;) – Andrey Tyukin May 17 '18 at 14:33
  • Yes thanks :) An address because it's about mailing, but agreed that `text` does not really fit. – Mik378 May 17 '18 at 14:35
  • This isn't really a curried function, it is just a higher-order-function(HOF), i.e. a function that returns a function. The intention is clearly that the `EmailFilter` would be called multiple times, once for each email. With currying you convert a single function with multiple arguments into multiple functions with a single argument. When you call a curried function you call all the functions in turn, you don't re-use any of the intermediate functions. – Tim May 17 '18 at 15:40
  • `minimumSize` is a curried one by definition even if the intention is to use it as a higher-order function. – Mik378 May 17 '18 at 15:40
  • @Mik378 I'd be interested to see that definition. If this is a curried function, then the uncurried function is `minimumSize(Int, Email): Boolean` which is a badly named function because it is just a comparison operation. I do not believe that the function was intended to be called as `minimumSize(10)(email)` but rather `emails.filter(minimumSize(10))` and therefore I do not believe that it is a curried function. YMMV – Tim May 17 '18 at 15:54
  • https://hughfdjackson.com/javascript/why-curry-helps/ Don’t confuse intention and nature. – Mik378 May 17 '18 at 15:56
  • @Mik378 I am not confused, I just don't want people to think that any single-argument function that returns a single-argument function is by definition a curried version of a two-argument function. Describing a function as curried implies intent, and I don't see any evidence of that intent. YMMV – Tim May 17 '18 at 16:20
  • @Tim It is a curried. Take a look at this video of Runar Bjarnason: https://youtu.be/ZasXwtTRkio at exactly 3:50 and listen to what he says at that moment. (between 3:50 and 3:55). By the way what your emails.filter(minimumSize(10)) does is a curring since a call of minimumSize(10)(currentEmail) will occur internally. – Mik378 May 17 '18 at 16:24
  • @Mik378 I looked. What the presenter calls currying is actually partial application, so I'm not sure how that helps. – Tim May 17 '18 at 17:05
  • Goal of currying is to defer the application of the function. It is similar to partially apply a function returning 1-unary nested function. Currying always produces nested unary (1-ary) functions. The transformed function is still largely the same as the original. Partial application produces functions of arbitrary arity. The transformed function is different from the original – it needs less arguments. – Mik378 May 17 '18 at 17:18