2

I have a function in a context, (in a Maybe / Option) and I want to pass it a value and get back the return value, directly out of the context.

Let's take an example in Scala :

scala> Some((x:Int) => x * x)
res0: Some[Int => Int] = Some(<function1>)

Of course, I can do

res0.map(_(5))

to execute the function, but the result is wrapped in the context.

Ok, I could do :

res0.map(_(5)).getOrElse(...)

but I'm copy/pasting this everywhere in my code (I have a lot of functions wrapped in Option, or worst, in Either...).

I need a better form, something like :

res0.applyOrElse(5, ...)

Does this concept of 'applying a function in a concept to a value and immediatly returning the result out of the context' exists in FP with a specific name (I'm lost in all those Functor, Monad and Applicatives...) ?

M'λ'
  • 651
  • 9
  • 21
  • This is a little like `Kleisli`'s `unliftId`, but instead of a comonad instance you want to provide a default value. I'm not sure off the top of my head whether Scalaz provides a convenient way to do that. – Travis Brown Jul 21 '15 at 01:12

3 Answers3

2

You can use andThen to move the default from the place where you call the function to the place where you define it:

val foo: String => Option[Int] = s => Some(s.size)
val bar: String => Int = foo.andThen(_.getOrElse(100))

This only works for Function1, but if you want a more generic version, Scalaz provides functor instances for FunctionN:

import scalaz._, Scalaz._

val foo: (String, Int) => Option[Int] = (s, i) => Some(s.size + i)
val bar: (String, Int) => Int = foo.map(_.getOrElse(100))

This also works for Function1—just replace andThen above with map.

More generally, as I mention above, this looks a little like unliftId on Kleisli, which takes a wrapped function A => F[B] and collapses the F using a comonad instance for F. If you wanted something that worked generically for Option, Either[E, ?], etc., you could write something similar that would take a Optional instance for F and a default value.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • This is what I was expecting (more or less). Thank you. Peter Neyens answer was really interesting too, so I'm selecting this one as the accepted answer, but Peter one is worth considering too. – M'λ' Jul 21 '15 at 06:37
1

You could write something like applyOrElse using Option.fold.

fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B

val squared = Some((x:Int) => x * x)
squared.fold { 
  // or else = ifEmpty
  math.pow(5, 2).toInt
}{
  // execute function
  _(5)
}

Using Travis Browns recent answer on another question, I was able to puzzle together the following applyOrElse function. It depends on Shapeless and you need to pass the arguments as an HList so it might not be exactly what you want.

def applyOrElse[F, I <: HList, O](
  optionFun: Option[F],
  input: I,
  orElse: => O
)(implicit
  ftp: FnToProduct.Aux[F, I => O]
): O = optionFun.fold(orElse)(f => ftp(f)(input))

Which can be used as :

val squared = Some((x:Int) => x * x)
applyOrElse(squared, 2 :: HNil, 10)
// res0: Int = 4

applyOrElse(None, 2 :: HNil, 10)
// res1: Int = 10

val concat = Some((a: String, b: String) => s"$a $b")
applyOrElse(concat, "hello" :: "world" :: HNil, "not" + "executed")
// res2: String = hello world
Community
  • 1
  • 1
Peter Neyens
  • 9,770
  • 27
  • 33
  • I have added an `applyOrElse` function using shapeless. Somebody better versed in shapeless might be able to improve it ... – Peter Neyens Jul 20 '15 at 20:08
  • This may be overkill since the OP doesn't specify that support for `FunctionN` is necessary, and even then it's probably a little cleaner to use something like a `Functor` instance for `FunctionN`. – Travis Brown Jul 21 '15 at 01:18
  • Thank you both for this interesting approach ; FunctionN could be required sometimes (I wanted to start with a simple case), so this is definetly usefull. But for most of my cases, this is overkill, yes ! :) – M'λ' Jul 21 '15 at 06:35
0

The getOrElse is most logical way to do it. In regards to copy/pasting it all over the place - you might not be dividing your logic up on the best way. Generally, you want to defer resolving your Options (or Futures/etc) in your code until the point you need to have it unwrapped. In this case, it seems more sensible that your function takes in an an Int and returns an Int, and you map your option where you need the result of that function.

Ren
  • 3,395
  • 2
  • 27
  • 46