Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a method which returns one or two parameters generically

I have a method

def foo(num: Int): String

Where I call some in some places in my code, and everything was good.
Lately, I encountered a situation where I need to call the same method, but with some parameter int value, I need to get in return 2 Strings, and not only one. My current way of implementing it is:

def foo(num: Int): List[String]

Where each time I call foo and get 1 String, I will get the head of the list, and each time I call for and it returns 2 strings, I will get the elements in [0, 1] (I know that when I call foo(10), I get 2 strings, and for the rest - only one).

Is there a more idiomatic scala/functional for this?

like image 416
Dave B. Avatar asked Sep 15 '19 17:09

Dave B.


People also ask

Which of these type parameters is used for a generic methods to return and accept any type of object?

Which of these type parameters is used for a generic methods to return and accept any type of object? Explanation: T is used for type, A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.

Which of these type parameters is used for a generic class to return?

Which of these type parameters is used for a generic class to return and accept a number? Explanation: N is used for Number. Sanfoundry Certification Contest of the Month is Live.


2 Answers

Scala 2.13 introduced literal-based singleton types, so actually you can do a crazy thing like this:

def foo(num: 10): (String, String) = ("Hello", "World")
def foo(num: Int): String = s"Hello $num"

val (left, right) = foo(10)
val single = foo(2)

and it will compile and work.

Of course, you can return a list instead of a tuple for the 10 case if you wish.

It should also work for typelevel scala (even before 2.13).

With regular Lightbend Scala before 2.13 you could still do that, but it was a lot clunkier. It was necessary to use an additional trick involving using type called Witness, but fortunately, it is provided by shapeless:

import shapeless.Witness
import shapeless.syntax.singleton._

def foo(num: Witness.`10`.T): (String, String) = ("Hello", "World")
def foo(num: Int): String = s"Hello $num"

val (left, right) = foo(10)
val single = foo(2)

And of course it is necessary to add shapeless as dependency:

libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"

Another approach you could use is just the use of some kind of special container for your result. I would recommend tuple: (String, Option[String]). In case you're returning "regular" result, you would return (String, None) and in case of 10 you can return (String, Some[String]).

like image 186
Krzysztof Atłasik Avatar answered Oct 04 '22 15:10

Krzysztof Atłasik


Since the result space is cleanly split into two sides (disjointed) consider Either like so

def foo(i: Int): Either[(String, String), String] =
  if (i == 10) Left("one", "two") else Right("one")

val Left(Tuple2(x, y)) = foo(10)
val Right(a) = foo(2)

which is inspired by @Krzysztof's edit regarding "special container".

like image 28
Mario Galic Avatar answered Oct 04 '22 15:10

Mario Galic