I have a case class with two options
case class MyClass(
first: Option[String] = None,
second: Option[Boolean] = None
)
Is there a way in Scala to specify at the type level that both first and second can't both be set? So if MyClass.second.isDefined then MyCLass.first must be None and vice versa, or both can be None.
This is possible in TypeScript.
So, how about this (the following code is for scala3, but it is easy to implement in scala2):
scala> #annotation.implicitNotFound("Need (None, Some) or (Some, None), found (${F}, ${S})")
| trait Cons[F, S]
|
| object Cons:
| given Cons[Some[String], None.type] = new Cons {}
| given Cons[None.type, Some[Boolean]] = new Cons {}
|
| case class MyClass[F <: Option[String], S <: Option[Boolean]](first: F, second: S)(using c: Cons[F, S])
scala> val m1 = MyClass(None, Some(true))
val m1: MyClass[None.type, Some[Boolean]] = MyClass(None,Some(true))
scala> val m2 = MyClass(Some(""), None)
val m2: MyClass[Some[String], None.type] = MyClass(Some(),None)
scala> val m3 = MyClass(None, None)
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 |val m3 = MyClass(None, None)
| ^
| Need (None, Some) or (Some, None), found (None.type, None.type)
1 error found
scala> val m4 = MyClass(Some(""), Some(false))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 |val m4 = MyClass(Some(""), Some(false))
| ^
| Need (None, Some) or (Some, None), found (Some[String], Some[Boolean])
1 error found
Related
Okay, this is driving me nuts and I am certain I am missing something rather obvious here. Hoping more eyes can clue me on my problem. I have the following few lines of code where I am trying to break out a class that contains an Option[List] into individual entires:
case class Frob(t: String, ts: Option[List[String]])
case class FrobE(te: String, tes: String)
val f1 = Frob("G. Martin", Some(List("A", "B", "C")))
val f2 = Frob("A. Weir", Some(List("D", "E")))
val f3 = Frob("J. Tolkien", None)
val f4 = Frob("R. Jordan", None)
val f5 = Frob("G. Wolfe", Some(List("F", "Z")))
val fs = List(f1, f2, f3, f4, f5)
val fe = for {
f <- fs
vs <- f.ts
thing <- vs
} yield FrobE(f.t, thing)
But the compiler complains that the type of thing is List[FrobE] when it should be Option[Any]. Here is the error from ammonite:
cmd12.sc:4: type mismatch;
found : List[ammonite.$sess.cmd11.FrobE]
required: Option[?]
thing <- vs
^
Compilation Failed
I tried breaking this out into the equivalent flatMap/map chain, but am met with the same error (which is understandable):
# fs.flatMap(f => f.ts.flatMap(vs => vs.map(t => FrobE(f.t, t))))
cmd12.sc:1: type mismatch;
found : List[ammonite.$sess.cmd11.FrobE]
required: Option[?]
val fe2 = fs.flatMap(f => f.ts.flatMap(vs => vs.map(t => FrobE(f.t, t))))
^
Compilation Failed
If I make a little intermediate case class and break this into two steps as follows, then it works as I had hoped.
case class FrobI(ti: String, tis: List[String])
val fd = for {
f <- fs
vs <- f.ts
} yield FrobI(f.t, vs)
val fe2 = for {
d <- fd
v <- d.tis
} yield FrobE(d.ti, v)
This tells me is it something I am overlooking with the combination of Option[List[]] but I am really missing it at the moment and would love some advice.
Why not:
scala> case class Frob(t: String, ts: Option[List[String]])
| case class FrobE(te: String, tes: String)
| val f1 = Frob("G. Martin", Some(List("A", "B", "C")))
| val f2 = Frob("A. Weir", Some(List("D", "E")))
| val f3 = Frob("J. Tolkien", None)
| val f4 = Frob("R. Jordan", None)
| val f5 = Frob("G. Wolfe", Some(List("F", "Z")))
| val fs = List(f1, f2, f3, f4, f5)
|
| val fe = for {
| f <- fs
| thing <- f.ts.getOrElse(Nil)
| } yield FrobE(f.t, thing)
val fe: List[FrobE] = List(FrobE(G. Martin,A), FrobE(G. Martin,B), FrobE(G. Martin,C), FrobE(A. Weir,D),
FrobE(A. Weir,E), FrobE(G. Wolfe,F), FrobE(G. Wolfe,Z)
I have the following use case, in which I am iterating multiple times on the same collection, and every time I find a different item in that collection.
class Foo(policyToData: Map[String, MyClass]){
val milk: Option[MyClass] = policyToData.values.find(_.`type` == Milk)
val meat: Option[MyClass] = policyToData.values.find(_.`type` == Meat)
val bread: Option[MyClass] = policyToData.values.find(_.`type` == Bread)
val other: List[MyClass] = policyToData.values.filter(_.`type` == Other).toList
}
Is there a better way to do it? with one iteration?
If it's a large collection, folding into a map means you only build the collection of interest.
scala> case class C(name: String)
defined class C
scala> val cs = List(C("milk"),C("eggs"),C("meat"))
cs: List[C] = List(C(milk), C(eggs), C(meat))
scala> cs.foldLeft(Map.empty[String,C]) {
| case (m, c # C("milk" | "meat")) if !m.contains(c.name) => m + (c.name -> c)
| case (m, _) => m }
res5: scala.collection.immutable.Map[String,C] = Map(milk -> C(milk), meat -> C(meat))
then
scala> val milk = res5("milk")
milk: C = C(milk)
scala> val bread = res5.get("bread")
bread: Option[C] = None
The original groupBy solution was deleted because someone commented that it does extra work, but in fact it's a straightforward expression, if creating the intermediate Map of Lists is OK.
scala> cs.groupBy(_.name)
res0: scala.collection.immutable.Map[String,List[C]] = Map(meat -> List(C(meat)), eggs -> List(C(eggs)), milk -> List(C(milk)))
scala> res0.get("milk").map(_.head)
res1: Option[C] = Some(C(milk))
scala> res0.get("bread").map(_.head)
res2: Option[C] = None
or
scala> cs.filter { case C("milk" | "meat") => true case _ => false }.groupBy(_.name)
res4: scala.collection.immutable.Map[String,List[C]] = Map(meat -> List(C(meat)), milk -> List(C(milk)))
groupBy will do it:
val byType = list.groupBy(_.type).withDefaultValue(Nil)
val milk = byType(Milk).headOption
val other = byType(Other)
Etc ...
I am learning Scala at the moment
<pre>
scala> val sample = similarities.filter(m => {
| val movies = m._1
| (movieNames(movies._1).contains("Star Wars (1977)"))
| })
</pre>
sample: org.apache.spark.rdd.RDD[((Int, Int), Double)] = FilteredRDD[25] at filter at :36
The sample compiled just fine
But when I tried to call sample again in the next command
<pre>
scala> val result = sample.map(v => {
| val m1 = v._1._1
| val m2 = v._1._2
| val correl = v._2._1
| //val rcorr = v._2._2
| // val cos = v._2._3
| //val j = v._2._4
| (movieNames(m1), movieNames(m2), correl)
| })
<console>:41: error: value _1 is not a member of Double
val correl = v._2._1
</pre>
Can someone please help me.Thanks in advance
Given the amount of indexing on the composed tuple, consider wrapping ((Int, Int), Double) onto a case class and defining an implicit on it as follows,
case class Movie(m1: Int, m2: Int, correl: Double)
implicit def RichMovie(v: ((Int,Int),Double) ) = Movie(v._1._1, v._1._2, v._2)
Thus, given an instance of a composed tuple
scala> val m = ( (1,2), 3.5)
m: ((Int, Int), Double) = ((1,2),3.5)
we can access its members as follows,
scala> m.m1
res0: Int = 1
scala> m.m2
res1: Int = 2
scala> m.correl
res2: Double = 3.5
val correl = v._2._1
should be just
val correl = v._2
because it is part of the second element in the tuple ((Int, Int), Double)
It is pretty easy to write flatten(lol: List[List[T]]): List[T] which transforms a list of lists to a new list. Other "flat" collections (e.g. Set) seem to provide flatten too.
Now I wonder if I can define a flatten for Tree[T](defined as a T and list of Tree[T]s).
This is not perfect, just serves an example. All you need to do is to traverse a tree in depth-first or breadth-first manner and collect results. Pretty much the same as flatten for lists.
1) Define a tree structure (I know, I know it's not the best way to do it :)):
scala> case class Node[T](value: T, left: Option[Node[T]] = None,
| right: Option[Node[T]] = None)
defined class Node
2) Create a little tree:
scala> val tree = Node(13,
| Some(Node(8,
| Some(Node(1)), Some(Node(11)))),
| Some(Node(17,
| Some(Node(15)), Some(Node(25))))
| )
tree: Node[Int] = Node(13,Some(Node(8,Some(Node(1,None,None)),Some(Node(11,None,None)))),Some(Node(17,Some(Node(15,None,None)),Some(Node(25,None,None)))))
3) Implement a function that can traverse a tree:
scala> def observe[T](node: Node[T], f: Node[T] => Unit): Unit = {
| f(node)
| node.left foreach { observe(_, f) }
| node.right foreach { observe(_, f) }
| }
observe: [T](node: Node[T], f: Node[T] => Unit)Unit
4) Use it to define a function that prints all values:
scala> def printall = observe(tree, (n: Node[_]) => println(n.value))
printall: Unit
5) Finally, define that flatten function:
scala> def flatten[T](node: Node[T]): List[T] = {
| def flatten[T](node: Option[Node[T]]): List[T] =
| node match {
| case Some(n) =>
| n.value :: flatten(n.left) ::: flatten(n.right)
| case None => Nil
| }
|
| flatten(Some(node))
| }
flatten: [T](node: Node[T])List[T]
6) Let's test. First print all elems:
scala> printall
13
8
1
11
17
15
25
7) Run flatten:
scala> flatten(tree)
res1: List[Int] = List(13, 8, 1, 11, 17, 15, 25)
It's a sort of general purpose tree algorithm like tree traversal. I made it return Ts instead of Nodes, change it as you like.
I'm not sure how you want to define that flatten exactly, but you can look at the Scalaz Tree implementation:
https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Tree.scala
If you want flatten to return you list of all Tree nodes, then Scalaz already provides you what you want:
def flatten: Stream[A]
Result type is Stream instead of List, but this is not a problem I guess.
If you want something more sophisticated, then you can probably implement it using existing flatMap:
def flatMap[B](f: A => Tree[B]): Tree[B]
Let's say you have Tree of type Tree[Tree[A]] and want to flatten it to Tree[A]:
def flatten1: Tree[A] = flatMap(identity)
This will work also for other, more weird scenarios. For example, you can have Tree[List[A]], and want to flatten everything inside that Lists without affecting Tree structure itself:
def flatten2[B]: Tree[List[B]] = flatMap(l => leaf(l.flatten))
Looks like it works as expected:
scala> node(List(List(1)), Stream(node(List(List(2)), Stream(leaf(List(List(3, 4), List(5))))), leaf(List(List(4)))))
res20: scalaz.Tree[List[List[Int]]] = <tree>
scala> res20.flatMap(l => leaf(l.flatten)).drawTree
res23: String =
"List(1)
|
+- List(2)
| |
| `- List(3, 4, 5)
|
`- List(4)
"
It could be worth noting that scalaz Tree is also a Monad. If you will look at the scalaz/tests/src/test/scala/scalaz/TreeTest.scala you will see which laws are fulfilled for Tree:
checkAll("Tree", equal.laws[Tree[Int]])
checkAll("Tree", traverse1.laws[Tree])
checkAll("Tree", applicative.laws[Tree])
checkAll("Tree", comonad.laws[Tree])
I don't know why monad is not here, but if you will add checkAll("Tree", monad.laws[Tree]) and run tests again, they will pass.
If I'm understanding the question correctly, you want to define Tree like so:
case class Tree[T]( value:T, kids:List[Tree[T]] )
First, I wouldn't want to use ::: in the solution because of the performance implications. Second, I'd want to do something much more general -- define a fold operator for the type, which can be used for all sorts of things -- and then simply use a fold to define flatten:
case class Tree[T]( value:T, kids:List[Tree[T]] ) {
def /:[A]( init:A )( f: (A,T) => A ):A =
( f(init,value) /: kids )( (soFar,kid) => ( soFar /: kid )(f) )
def flatten =
( List.empty[T] /: this )( (soFar,value) => value::soFar ).reverse
}
Here's a test:
scala> val t = Tree( 1, List( Tree( 2, List( Tree(3,Nil), Tree(4,Nil) ) ), Tree(5,Nil), Tree( 6, List( Tree(7,Nil) ) ) ) )
t: Tree[Int] = Tree(1,List(Tree(2,List(Tree(3,List()), Tree(4,List()))), Tree(5,List()), Tree(6,List(Tree(7,List())))))
scala> t.flatten
res15: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
I consider refactoring few method signatures that currently take parameter of type List or Set of concrete classes --List[Foo]-- to use repeated parameters instead: Foo*.
Update: Following reasoning is flawed, move along...
This would allow me to use the same method name and overload it based on the parameter type. This was not possible using List or Set, because List[Foo] and List[Bar] have same type after erasure: List[Object].
In my case the refactored methods work fine with scala.Seq[Foo] that results from the repeated parameter. I would have to change all the invocations and add a sequence argument type annotation to all collection parameters: baz.doStuffWith(foos:_*).
Given that switching from collection parameter to repeated parameter is semantically equivalent, does this change have some performance impact that I should be aware of?
Is the answer same for scala 2.7._ and 2.8?
When Scala is calling a Scala varargs method, the method will receive an object that extends Seq. When the call is made with : _*, the object will be passed as is*, without copying. Here are examples of this:
scala> object T {
| class X(val self: List[Int]) extends SeqProxy[Int] {
| private val serial = X.newSerial
| override def toString = serial.toString+":"+super.toString
| }
| object X {
| def apply(l: List[Int]) = new X(l)
| private var serial = 0
| def newSerial = {
| serial += 1
| serial
| }
| }
| }
defined module T
scala> new T.X(List(1,2,3))
res0: T.X = 1:List(1, 2, 3)
scala> new T.X(List(1,2,3))
res1: T.X = 2:List(1, 2, 3)
scala> def f(xs: Int*) = xs.toString
f: (Int*)String
scala> f(res0: _*)
res3: String = 1:List(1, 2, 3)
scala> f(res1: _*)
res4: String = 2:List(1, 2, 3)
scala> def f(xs: Int*): Seq[Int] = xs
f: (Int*)Seq[Int]
scala> def f(xs: Int*) = xs match {
| case ys: List[_] => println("List")
| case _ => println("Something else")
| }
f: (Int*)Unit
scala> f(List(1,2,3): _*)
List
scala> f(res0: _*)
Something else
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
scala> def f(xs: Int*) = xs match {
| case ys: List[_] => println("List")
| case zs: ArrayBuffer[_] => zs.asInstanceOf[ArrayBuffer[Int]] += 4; println("Array Buffer")
| case _ => println("Something else")
| }
f: (Int*)Unit
scala> val ab = new ArrayBuffer[Int]()
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala> ab + 1
res11: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)
scala> ab + 2
res12: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2)
scala> ab + 3
res13: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)
scala> f(ab: _*)
Array Buffer
scala> ab
res15: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
Note
An Array is passed as a WrappedArray. There's no copying of elements involved, however, and changes to the WrappedArray will be reflected in the Array.
Your reason for replacing List[T] with T* is flawed: Scala will not allow overloading like
class Foo
{
def t1(x : Int*) = println("Ints")
def t1(x : Strings*) = println("Strings")
}
This will result in the same compiler error as using List[Int]/List[String] here.
Although a bit clumsy you could use
class Foo
{
def t1(x0 : Int,x : Int*) = println("Ints")
def t1(x0 : String,x : Strings*) = println("Strings")
}
but that requires special treatment of the first parameter versus the rest.
Gr. Silvio
In the simplest terms, all the arguments that correspond to a repeated formal parameters, regardless of their origin, must be copied to a sequential collection of some sort for presentation to the method. The details of exactly what kind of sequence is used vary with Scala version and possibly with the source of the arguments. But regardless of those details, it is an O(n) operation, though the cost per item is pretty low. There will be at least one and sometimes more instance allocations for the sequence itself.
Randall Schulz