I think I understand what sequence is. I am wondering why it does not work with List[ValidationNel]. For instance:
The sequence works fine with List[Option]]
scala> val os = List(1.some, 2.some)
os: List[Option[Int]] = List(Some(1), Some(2))
scala> os.sequence
res10: Option[List[Int]] = Some(List(1, 2))
... but does not work with List[ValidationNel]
scala> val vs: List[ValidationNel[String, Int]] = List(Success(1), Success(2))
vs: List[scalaz.ValidationNel[String,Int]] = List(Success(1), Success(2))
scala> vs.sequence
<console>:15: error: could not find implicit value for parameter ev:scalaz.Leibniz.===[scalaz.ValidationNel[String,Int],G[B]]
... however sequenceU does work with List[ValidationNel]
scala> vs.sequenceU
res14: scalaz.Validation[scalaz.NonEmptyList[String],List[Int]] = Success(List(1, 2))
My questions are: Why does not sequence work with List[ValidationNel] ? Why does sequenceU work with it ?
.sequenceU uses the Unapply technique to derive the correct types, where as for .sequence you need to manually provide the types for it.
To make things a bit more annoying, the first type argument of sequence needs a type parameter that takes one type parameter not two like ValidationNel. So you either type lambda it, or do a local type definition.
Try
type X = ValidationNel[String,X]
vs.sequence[X, Int]
or
vs.sequence[({type l[A]=ValidationNel[String,A]})#l,Int]
Not an expert on Scalaz.
To be short, because ValidationNel is not monad, so this conversion is not valid:
List[ValidationNel[SomeType]] => ValidationNel[List[SomeType]]
as the error message shows: implicit value not found, which indicates no such conversion.
Related
What is the difference between passing two arguments vs passing one argument?
val option1 = Option(String,String)
and
val option2 = Option(String)
When you write something like Option(1, 2), the compiler first desugars this to Option.apply(1, 2), and then when it sees that the Option companion object doesn't have an apply method that takes two arguments, it automatically converts the arguments into a tuple:
scala> Option(1, 2)
res0: Option[(Int, Int)] = Some((1,2))
It would do something similar for Option(1, 2, 3), Option(1, 2, 3, 4), etc.
This is known as auto-tupling and only works for methods with a single argument. For example, the following won't compile:
scala> def foo[T](t: T, u: T): T = t
foo: [T](t: T, u: T)T
scala> foo(1, 2, 3)
<console>:13: error: too many arguments for method foo: (t: T, u: T)T
foo(1, 2, 3)
^
This "feature" is provided for syntactic convenience, and it brings Scala a little closer (at least in a superficial way) to other functional languages where tuples and function argument lists are more unified. Lots of people hate auto-tupling, though, because these things aren't actually unified in Scala, and pretending they are can lead to confusing code and annoying error messages. If you're one of these people (I am), you can turn on the -Ywarn-adapted-args compiler flag, which gives you warnings when the compiler tries to do this:
scala> Option(1, 2)
<console>:12: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
signature: Option.apply[A](x: A): Option[A]
given arguments: 1, 2
after adaptation: Option((1, 2): (Int, Int))
Option(1, 2)
^
res0: Option[(Int, Int)] = Some((1,2))
This is a matter of taste, though.
There is no overload of Option.apply with two arguments, so the simple difference between the two is that the first one gives an error related to the number of arguments:
error: too many arguments (2) for method apply: (x: A)Option[A] in object Option
Whereas the second one gives an error because String is a type, not a value, and you are trying to pass a type as an argument, which is impossible:
error: object java.lang.String is not a value
This question already has answers here:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
(11 answers)
Closed 6 years ago.
I'm tying to write a generic function that will convert any type to Option[T]
but somehow it's not working as expected
scala> def toOption[T](obj:Any):Option[T] = obj match {
| case obj:T => Some(obj)
| case _ => None
| }
<console>:11: warning: abstract type pattern T is unchecked since it is eliminated by erasure
case obj:T => Some(obj)
^
toOption: [T](obj: Any)Option[T]
here it's seems ok it return the Option[String]
scala> toOption[String]("abc")
res0: Option[String] = Some(abc)
but here it return Some(def) instead of None
scala> toOption[Int]("def")
res1: Option[Int] = Some(def)
i can't seem to figure the appropriate way to create this generic function ,nor to understand why is that happens, I've already read many posts and questions about type erasure in scala but still can't get it, specific explanation would be a great help!
The type T is not available at runtime because Scala uses type erasure for generics like Java does. So your test case obj:T has not the desired effects and the compiler warned you about that fact.
You may use ClassTags to make the type information available at runtime. ClassTag already implements the conversion to Optional in its unapply method:
scala> import scala.reflect.{classTag,ClassTag};
import scala.reflect.{classTag, ClassTag}
scala> def toOption[T: ClassTag](obj: Any): Option[T] = classTag[T].unapply(obj);
toOption: [T](obj: Any)(implicit evidence$1: scala.reflect.ClassTag[T])Option[T]
scala> toOption[Int](1)
res1: Option[Int] = Some(1)
scala> toOption[String](1)
res2: Option[String] = None
scala> toOption[String]("one")
res3: Option[String] = Some(one)
scala> toOption[Int]("one")
res4: Option[Int] = None
But this doesn't work for generic types as you see here:
scala> toOption[List[Int]](List("one", "two"))
res5: Option[List[Int]] = Some(List(one, two))
scala> res5.get
res6: List[Int] = List(one, two)
scala> res6(0) + res6(1)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
... 29 elided
You might want to have two type arguments of the function - the desired type and the actual type:
def toOption[Desired, Actual](obj:Actual):Option[Desired] = ???
And then, I guess, you want to return Some only if the types match:
def toOption[Desired, Actual](obj:Actual)
(implicit ev: Actual =:= Desired = null):Option[Desired] =
if(ev == null)
None
else
Some(ev(obj))
Here you have two type arguments, that might be inconvenient sometimes, when Scala cannot infer both of the arguments. For the same syntax as you used (giving single type argument), you may use the following trick:
class toOptionImpl[Desired] {
def apply[Desired, Actual](obj:Actual)
(implicit ev: Actual =:= Desired = null):Option[Desired] =
if(ev == null)
None
else
Some(ev(obj))
}
def toOption[Desired] = new toOptionImpl[Desired]
It might be used the same way:
toOption[String]("def") == Some("def")
toOption[Int]("def") == None
(You might also look into Miles Sabin's polymorphic functions https://milessabin.com/blog/2012/04/27/shapeless-polymorphic-function-values-1/, https://milessabin.com/blog/2012/05/10/shapeless-polymorphic-function-values-2/)
Given
val strings = Set("Hi", "there", "friend")
def numberOfCharsDiv2(s: String) = scala.util.Try {
if (s.length % 2 == 0) s.length / 2 else throw new RuntimeException("grr")
}
Why can't I flatMap away the Try resulting from the method call? i.e.
strings.flatMap(numberOfCharsDiv2)
<console>:10: error: type mismatch;
found : scala.util.Try[Int]
required: scala.collection.GenTraversableOnce[?]
strings.flatMap(numberOfCharsDiv2)
or
for {
s <- strings
n <- numberOfCharsDiv2(s)
} yield n
<console>:12: error: type mismatch;
found : scala.util.Try[Int]
required: scala.collection.GenTraversableOnce[?]
n <- numberOfCharsDiv2(s)
However if I use Option instead of Try there's no problem.
def numberOfCharsDiv2(s: String) = if (s.length % 2 == 0)
Some(s.length / 2) else None
strings.flatMap(numberOfCharsDiv2) # => Set(1, 3)
What's the rationale behind not allowing flatMap on Try?
Let's look at the signature of flatMap.
def flatMap[B](f: (A) => GenTraversableOnce[B]): Set[B]
Your numberOfCharsDiv2 is seen as String => Try[Int]. Try is not a subclass of GenTraversableOnce and that is why you get the error. You don't strictly need a function that gives a Set only because you use flatMap on a Set. The function basically has to return any kind of collection.
So why does it work with Option? Option is also not a subclass of GenTraversableOnce, but there exists an implicit conversion inside the Option companion object, that transforms it into a List.
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
Then one question remains. Why not have an implicit conversion for Try as well? Because you will probably not get what you want.
flatMap can be seen as a map followed by a flatten.
Imagine you have a List[Option[Int]] like List(Some(1), None, Some(2)). Then flatten will give you List(1,2) of type List[Int].
Now look at an example with Try. List(Success(1), Failure(exception), Success(2)) of type List[Try[Int]].
How will flatten work with the failure now?
Should it disappear like None? Then why not work directly with Option?
Should it be included in the result? Then it would be List(1, exception, 2). The problem here is that the type is List[Any], because you have to find a common super class for Int and Throwable. You lose the type.
These should be reasons why there isn't an implicit conversion. Of course you are free to define one yourself, if you accept the above consequences.
The problem is that in your example, you're not flatmapping over Try. The flatmap you are doing is over Set.
Flatmap over Set takes a Set[A], and a function from A to Set[B]. As Kigyo points out in his comment below this isn't the actual type signature of flatmap on Set in Scala, but the general form of flat map is:
M[A] => (A => M[B]) => M[B]
That is, it takes some higher-kinded type, along with a function that operates on elements of the type in that higher-kinded type, and it gives you back the same higher-kinded type with the mapped elements.
In your case, this means that for each element of your Set, flatmap expects a call to a function that takes a String, and returns a Set of some type B which could be String (or could be anything else).
Your function
numberOfCharsDiv2(s: String)
correctly takes a String, but incorrectly returns a Try, rather then another Set as flatmap requires.
Your code would work if you used 'map', as that allows you to take some structure - in this case Set and run a function over each element transforming it from an A to a B without the function's return type conforming to the enclosing structure i.e. returning a Set
strings.map(numberOfCharsDiv2)
res2: scala.collection.immutable.Set[scala.util.Try[Int]] = Set(Success(1), Failure(java.lang.RuntimeException: grr), Success(3))
It is a Monad in Scala 2.11:
scala> import scala.util._
import scala.util._
scala> val x: Try[String] = Success[String]("abc")
x: scala.util.Try[String] = Success(abc)
scala> val y: Try[String] = Failure[String](new Exception("oops"))
y: scala.util.Try[String] = Failure(java.lang.Exception: oops)
scala> val z = Try(x)
z: scala.util.Try[scala.util.Try[String]] = Success(Success(abc))
scala> val t = Try(y)
t: scala.util.Try[scala.util.Try[String]] = Success(Failure(java.lang.Exception: oops))
scala> z.flatten
res2: scala.util.Try[String] = Success(abc)
scala> t.flatten
res3: scala.util.Try[String] =
Failure(java.lang.UnsupportedOperationException: oops)
Kigyo explains well why Scala does not do this implicitly. To put it simply, Scala does not want to automatically throw away the exception that is preserved in a Try.
Scala does provide a simple way to explicitly translate a Try into an Option though. This is how you can use a Try in a flatmap:
strings.flatMap(numberOfCharsDiv2(_).toOption)
I'm confused as how isInstanceOf works in Scala. If I do something like this:
val x: Int = 5
x.isInstanceOf[Int]
Given that Scala does type erasure, shouldn't the JVM remove all type information during runtime?
It's not all type information, just information about generic types. Consider this:
scala> val l = List("foo")
l: List[String] = List(foo)
scala> l.isInstanceOf[List[String]]
res0: Boolean = true
scala> l.isInstanceOf[List[Int]]
<console>:9: warning: fruitless type test: a value of type List[String] cannot also be a List[Int] (the underlying of List[Int]) (but still might match its erasure)
l.isInstanceOf[List[Int]]
^
res1: Boolean = true
They both return true, because the erased type is List.
I suddenly came across this (unexpected to me) situation:
def method[T](x: T): T = x
scala> method(1)
res4: Int = 1
scala> method(1, 2)
res5: (Int, Int) = (1,2)
Why in case of two and more parameters method returns and infers a tuple but throwing error about parameter list?
Is it by intention? Maybe this phenomenon has a name?
Here is the excerpt from scala compiler:
/** Try packing all arguments into a Tuple and apply `fun'
* to that. This is the last thing which is tried (after
* default arguments)
*/
def tryTupleApply: Option[Tree] = ...
And here is related issue: Spec doesn't mention automatic tupling
It all means that in the above written example (type-parameterized method of one argument) scala tries to pack parameters into tuple and apply function to that tuple. Further from this two short pieces of information we may conclude that this behaviour not mentioned in language specification, and people discuss to add compiler warnings for cases of auto-tupling. And that this may be called auto-tupling.
% scala2.10 -Xlint
scala> def method[T](x: T): T = x
method: [T](x: T)T
scala> method(1)
res1: Int = 1
scala> method(1, 2)
<console>:9: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
signature: method[T](x: T): T
given arguments: 1, 2
after adaptation: method((1, 2): (Int, Int))
method(1, 2)
^
res2: (Int, Int) = (1,2)