2016-06-10 5 views
3

Я довольно новичок в Scala и наткнулся на небольшую небольшую проблему, которая меня беспокоит. Скажем, есть какой-то метод с параметром по умолчаниюВызов метода с параметром или параметром по умолчанию в Scala

def foo(v: Any = "default"): String = s"called with parameter '$v'" 

и Option val opt: Option[String]. Как вызвать этот метод либо с помощью значения параметра (если определено), либо по умолчанию? То есть, несмотря на очевидное решение

val result = if (opt.isDefined) 
       from.here.to.foo(opt.get) 
      else 
       from.here.to.foo() 

и того, чтобы ввести этот метод с (возможно долго) объекта цепи дважды? Не говоря уже о наличии более чем одного дополнительного параметра/по умолчанию ...


Все, что я мог придумать это беспомощному Helper

def definedOrDefault[A, B](opt: Option[A], f0: => B, f1: A => B): B = 
    if (opt.isDefined) f1(opt.get) else f0 

но когда не в состоянии упомянуть параметры по умолчанию в более высоком порядке функции ... вот и все. Напоминает мне о плохих старых днях с Java, где перегрузка метода создает ту же проблему.

ответ

3

Кажется, что вы используете два понятия значения, являющихся необязательными в одном месте, то есть необязательный параметр и Option. Им не нравится хорошо играть вместе, может быть, лучше использовать только один.

Если вы всегда просто передаете значение Option методу или ничего не передаете, чтобы получить значение по умолчанию, возможно, подумайте об изменении функции, чтобы принять Option.

def foo(v: Option[String]) = { 
    val param = v getOrElse "default" 
    s"called with parameter '$param'" 
} 

Если вы все еще хотите иметь параметр по умолчанию можно изменить подпись

def foo(v: Option[String] = None) 

Этот подход, однако, потребует от вас обернуть параметр в Some, когда вы делаете регулярный вызов, например,

foo(Some("regular param")) 

но он хорошо работает, когда вы используете Option s. Вы можете легко добавить дополнительные параметры.

Вот пример

def foo(v1: Option[String] = None, v2: Option[Int] = None) = { 
    val param1 = v1 getOrElse "default" 
    val param2 = v2 getOrElse 42 
    s"'$param1', '$param2'" 
} 

foo() // "default", 42 

foo(v2 = Some(12)) // "default", 12 

foo(Some("asd"), Some(11)) // "asd", 11 

val optionFromSomewhere = Some("a") 
val anotherOptionFromSomewhere = Option.empty[Int] 
foo(optionFromSomewhere, anotherOptionFromSomewhere) // "a", 42 

Можно также ввести неявное преобразование из Any в Option, что позволит вам пропустить Some, но я не думаю, что это такая хорошая идея

implicit def any2option[A](e: A): Option[A] = Some(e) 
+0

Вы можете даже установить параметр «по умолчанию» в этом случае на «Нет», чтобы сохранить некоторые из тех же семантик. – wheaties

+0

Да, я тоже это понял. Ответ был проделан: p –

+0

Я надеялся на решение, в котором метод можно оставить нетронутым, он может быть частью библиотеки и недоступен. И проверка параметров сначала нарушает функциональный стиль. Но все равно приятно! – ABika

0

Вы можете использовать функцию map, определенную на Option, чтобы преобразовать значение, заключенное внутри, если оно определено. Это выглядит следующим образом:

opt.map(foo) 
// same as 
opt.map(x => foo(x)) 

Это будет возвращать либо Option[String] если некоторое значение было внутри, или None, если он был None раньше.

Вот полный пример из РЕПЛА:

scala> def foo(v: Any = "default"): String = s"called with parameter '$v'" 
foo: (v: Any)String 

scala> Some("Hello") 
res0: Some[String] = Some(Hello) 

scala> res0.map(foo) 
res1: Option[String] = Some(called with parameter 'Hello') 

scala> val x: Option[String] = None 
x: Option[String] = None 

scala> x.map(foo) 
res2: Option[String] = None 

EDIT:

Назвать это значение по умолчанию, вы должны соответствовать вашему выбору до вашего звонка, так как метод ожидает не Option параметр.

val optOrDefault = opt getOrElse "default" 
foo(optOrDefault) 
+0

Я думаю, что OP хотел бы видеть '' вызванный с параметром по умолчанию '', когда' Option' пуст. Для этого потребуется 'opt map foo getOrElse foo()', что немного лучше, но для этого требуется набрать 'foo' дважды и не масштабируется до более дополнительных параметров. –

+0

Хм, возможно, вы правы. Я продолжу свой ответ, спасибо. –

1

Незначительного расширение @ ответ Лукаша, который слишком велик, чтобы поместиться в комментарии:

Вы можете избежать того, чтобы обернуть текущие параметры в Some без опасного any2option путем создания типа специального назначения:

sealed trait OptParam[+A] { def toOption: Option[A] } 
case class Param[+A](value: A) extends OptParam[A] { def toOption = Some(value) } 
case object NoParam extends OptParam[Nothing] { def toOption = None } 

object OptParam { 
    implicit def any2optParam[A](x: A): OptParam[A] = Param(x) 
} 

def foo(v1: OptParam[String] = NoParam, v2: OptString[Int] = NoParam) = { 
    val param1 = v1.toOption.getOrElse("default") 
    val param2 = v2.toOption.getOrElse(42) 
    s"'$param1', '$param2'" 
} 

foo("a") // "'a', '42'" 

что делает это более безопасным является то, что OptParam должны всегда выступать в качестве типа параметра метода, поэтому преобразование не получить срабатывает, когда вы не ожидаете.