Let us suppose, we have to create the OddList[+T]
which contains only odd number of elements. Now can we do something like this
OddList(1,2) //Works fine
OddList(1,2,3) //Compilation error
if there is no condition of odd/even then we would simply do as below
object OddList{
def apply[T](eles: T*) = ...
}
But can we control number of arguments that could be passed?
I'm not aware of any way of limiting this using some kind of built-in mechanism. A macro can be a possible solution. If you want to keep it simple, the only thing I can think of is that of asking for pairs:
object OddList {
def apply[T](elems: (T, T)*) = ???
}
I also think that there might be some confusion with regards to naming, since odd numbers are 2n+1 and even ones are 2n (because you can split them evenly by two -- see Wikipedia ). As such, you can define the following:
object EvenList {
def apply[T](elems: (T, T)*) = ???
}
object OddList {
def apply[T](elem: T, elems: (T, T)*) = ???
}
Probably not the most elegant solution, but definitely simple and easy to implement.
If this approach works for you, you can create quite flexible smart constructors as follows:
object Even {
def apply[CC[_], A](
elems: (A, A)*
)(implicit factory: collection.Factory[A, CC[A]]): CC[A] = {
val builder = factory.newBuilder
for ((first, second) <- elems) {
builder += first
builder += second
}
builder.result()
}
}
object Odd {
def apply[CC[_], A](elem: A, elems: (A, A)*)(implicit
factory: collection.Factory[A, CC[A]]
): CC[A] = {
val builder = factory.newBuilder
builder += elem
for ((first, second) <- elems) {
builder += first
builder += second
}
builder.result()
}
}
assert(Even[Set, Int]((1, 2), (3, 4)) == Set(1, 2, 3, 4))
assert(Odd[List, String]("1", ("2", "3")) == List("1", "2", "3"))
You can play around with this code here on Scastie .
As a further improvement, I believe you can also create types to wrap the two possible returned items so that you can reason about collection size parity at compile time (if that is a requirement), but this is not covered by this example.
One question first off: Are you including Nil
as an element?
Because (1 :: 2 :: Nil).size
is 2
/even, your other example is 3
/odd
At compile time , shapeless
has Sized
(see this answer: Scala, enforce length of Array/Collection parameter ), but that seems to be for static, concrete values.
At runtime you can throw an IllegalArgumentException
when the constructor is called
case class OddList[T]( elements: T* ) {
private val numElements: Int = elements.size
private val hasOddNumElements: Boolean = numElements % 2 != 0
require(hasOddNumElements, s"There are an even [$numElements] number of elements!")
}
OddList( 1, 2 ) // Runtime exception
OddList( 1, 2, 3 ) // Works fine
Thanks @Dylan and everyone who asked to explore to macros way. I don't know if there are better ways, I would love to know but macros way works for me, at least for now. I tried the below code and it worked.
trait OddArgHolder[T]{
val args: Seq[T]
}
case class OddArgs[T](args: Seq[T]) extends OddArgHolder[T]
object OddArgHolder{
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
def applyMacro[T](c: Context)(method: c.Expr[T]*): c.Expr[OddArgHolder[T]] = {
import c.universe._
if(method.size % 2 == 0){
c.abort(c.enclosingPosition, "Only odd number of arguments allowed")
} else {
val met = method.map(m => c.typecheck(m.tree))
c.Expr(c.parse(s"OddArgs( $met )"))
}
}
def apply[T](method: T*): OddArgHolder[T] = macro applyMacro[T]
}
println(OddArgHolder(1,2))
//Compiletime output:
//Only odd number of arguments allowed
// println(OddArgHolder(1,2))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.