I've met some problems with the sequence method. Here is the code,
illustrating my case:
import scalaz._
import Scalaz._
object Start {
def main(args : Array[String]) {
println(
validate(untrustedData)
)
}
def validate(ls: Val[List[String]]): Val[List[String]] = {
ls >>= { l: List[String] =>
(l map (_.parseInt.liftFailNel)).sequence[Val, Int]
}
}
def untrustedData: Val[List[String]] = List("123", "456", "78q").successNel
type Val[T] = ValidationNEL[Exception, T]
}
When i'm trying to compile this, i get next error from the compiler:
/home/zan/projects/idea-ws/scala-gradle/src/main/scala/Test.scala:13:
error: ambiguous implicit values:
both method ValidationApplicative in object Applicative of type
[X](implicit evidence$23:
scalaz.Semigroup[X])scalaz.Applicative[[B]scalaz.Validation[X,B]]
and method applicative in object Applicative of type [Z[_]](implicit
p: scalaz.Pure[Z],implicit a: scalaz.Apply[Z])scalaz.Applicative[Z]
match expected type scalaz.Applicative[Start.Val]
(l map (_.parseInt.liftFailNel)).sequence[Val, Int]
The only workaround i've found, is to pass all parameters to the
sequence explicitly, like:
.sequence[Val, Int](
a = conforms,
t = Traverse.TraversableTraverse,
n = Applicative.ValidationApplicative
)
But it looks really terrible. Am I missing something? May be there is
a better way?
By the way, is it possible to use Validation in for-comprehensions
with side-effects only? I mean something like this:
for {
a <- getA;
b <- getB; } {
writeToDB(a, b)
}
When i'm trying this kind of statements, compiler complains that there
is no Each instance for Validation.
(Sorry asking this question again. Possibly it has very obvious
solution, but i can't find it out.)
--
Regards,
Alexey Zlobin.
What versions of Scala and Scalaz are you using? With Scala 2.8.1 and
Scalaz 6.0-SNAPSHOT, this works:
(l: List[String]) => (l map (_.parseInt.liftFailNel)).sequence[Val, Int]
We've recently made it harder for you to use Validation monadically. Here's why:
scala> List(1.fail[Int], 2.fail[Int])
res0: List[scalaz.Validation[Int,Int]] = List(Failure(1), Failure(2))
scala> res0.sequence[({type l[x]=Validation[Int, x]})#l, Int]
res1: scalaz.Validation[Int,List[Int]] = Failure(3)
scala> import Validation.Monad._ // I know what I'm doing!
import Validation.Monad._
scala> res0.sequence[({type l[x]=Validation[Int, x]})#l, Int]
res2: scalaz.Validation[Int,List[Int]] = Failure(1) // Errors are not
accumulated.
To get the right semantics, you would need to:
ls.>>=((l: List[String]) => {
(l map (_.parseInt.liftFailNel)).sequence[Val, Int] // this will
use Applicative.ValidationApplicative, and accumulate results.
})(Validation.Monad.apply)
> /home/zan/projects/idea-ws/scala-gradle/src/main/scala/Test.scala:13:
> error: ambiguous implicit values:
> both method ValidationApplicative in object Applicative of type
> [X](implicit evidence$23:
> scalaz.Semigroup[X])scalaz.Applicative[[B]scalaz.Validation[X,B]]
> and method applicative in object Applicative of type [Z[_]](implicit
> p: scalaz.Pure[Z],implicit a: scalaz.Apply[Z])scalaz.Applicative[Z]
> match expected type scalaz.Applicative[Start.Val]
> (l map (_.parseInt.liftFailNel)).sequence[Val, Int]
I can't reproduce that error.
> The only workaround i've found, is to pass all parameters to the
> sequence explicitly, like:
>
> .sequence[Val, Int](
> a = conforms,
> t = Traverse.TraversableTraverse,
> n = Applicative.ValidationApplicative
> )
BTW, another way to do this:
(a = implicitly, t = implicitly, n = Applicative.ValidationApplicative)
> By the way, is it possible to use Validation in for-comprehensions
> with side-effects only? I mean something like this:
> for {
> a <- getA;
> b <- getB; } {
> writeToDB(a, b)
> }
> When i'm trying this kind of statements, compiler complains that there
> is no Each instance for Validation.
I've just added foreach/Each to allow this. As a workaround, include a
`yield`, and then ignore the result of the for-comprehension.
> (Sorry asking this question again. Possibly it has very obvious
> solution, but i can't find it out.)
Implicit Search in Scala is usually not so obvious. Validation is a
really tricky one. Monadically, only the fail-fast approach makes
sense. But in the Applicative style, error accumulation is possible,
and really the essense of this data type. But because Monad extends
Applicative, you have to be really careful about which instance you're
using. If you're not sure, use scalac -Xprint:typer to show which
implicits are selected.
-jason
2011/2/18 Jason Zaugg <jza...@gmail.com>:
> On Thu, Feb 17, 2011 at 8:05 PM, Alexey Zlobin <alexey...@gmail.com> wrote:
>> I've met some problems with the sequence method. Here is the code,
>> illustrating my case:
>>
>> def validate(ls: Val[List[String]]): Val[List[String]] = {
>> ls >>= { l: List[String] =>
>> (l map (_.parseInt.liftFailNel)).sequence[Val, Int]
>> }
>> }
>>
>> type Val[T] = ValidationNEL[Exception, T]
>
> What versions of Scala and Scalaz are you using? With Scala 2.8.1 and
> Scalaz 6.0-SNAPSHOT, this works:
>
> (l: List[String]) => (l map (_.parseInt.liftFailNel)).sequence[Val, Int]
I use scalaz-core_2.8.0:5.0. It is the only version available in
public repos. Will the 6.0 version be released soon?
>
> We've recently made it harder for you to use Validation monadically. Here's why:
>
> scala> List(1.fail[Int], 2.fail[Int])
> res0: List[scalaz.Validation[Int,Int]] = List(Failure(1), Failure(2))
>
> scala> res0.sequence[({type l[x]=Validation[Int, x]})#l, Int]
> res1: scalaz.Validation[Int,List[Int]] = Failure(3)
>
> scala> import Validation.Monad._ // I know what I'm doing!
> import Validation.Monad._
>
> scala> res0.sequence[({type l[x]=Validation[Int, x]})#l, Int]
> res2: scalaz.Validation[Int,List[Int]] = Failure(1) // Errors are not
> accumulated.
>
> To get the right semantics, you would need to:
>
> ls.>>=((l: List[String]) => {
> (l map (_.parseInt.liftFailNel)).sequence[Val, Int] // this will
> use Applicative.ValidationApplicative, and accumulate results.
> })(Validation.Monad.apply)
Do you mean that in my example sequence actually has a 'fail first'
semantics, without error accumulation? It's a bit unclear what's going
on in the last application in your example ( here:
...})(Validation.Monad.apply) ).
Thank you. That's how I do this now. Hopefully 6.0 will be out soon,
so I'll be able to remove some noisy yields.
>
>> (Sorry asking this question again. Possibly it has very obvious
>> solution, but i can't find it out.)
>
> Implicit Search in Scala is usually not so obvious. Validation is a
> really tricky one. Monadically, only the fail-fast approach makes
> sense. But in the Applicative style, error accumulation is possible,
> and really the essense of this data type. But because Monad extends
> Applicative, you have to be really careful about which instance you're
> using. If you're not sure, use scalac -Xprint:typer to show which
> implicits are selected.
>
> -jason
>
--
Alexey Zlobin
You can try the snapshot versions. We're hoping for to finalize 6.0 in
early March.
http://scala-tools.org/repo-snapshots/org/scalaz/scalaz-core_2.8.1/6.0-SNAPSHOT/
> Do you mean that in my example sequence actually has a 'fail first'
> semantics, without error accumulation? It's a bit unclear what's going
> on in the last application in your example ( here:
> ...})(Validation.Monad.apply) ).
It might have, I didn't test with that version. The point is it was
hard to know in Scalaz 5.0, as both the Monad and Applicative for
Validation are in the implicit scope. I would encourage you to use
scalac -Xprint:typer to see exactly what is happening. Scala gets a
lot less magical once you can peer behind the curtains :)
In my example, the sequence was definitely error-accumulating, as only
the Applicative is in the implicit scope. However this can't be used
to call >>=, so I explicitly passed a Monad instance.
>> I've just added foreach/Each to allow this. As a workaround, include a
>> `yield`, and then ignore the result of the for-comprehension.
>
> Thank you. That's how I do this now. Hopefully 6.0 will be out soon,
> so I'll be able to remove some noisy yields.
We publish the snapshots after every successful build, so you can try
this out already. (Not that we're encouraging you to perform
side-effects!)
On 19/02/11 02:56, Alexey Zlobin wrote:
>
>> (l map (_.parseInt.liftFailNel)).sequence[Val, Int] // this will
>> use Applicative.ValidationApplicative, and accumulate results.
>> })(Validation.Monad.apply)
As a side note:
z.map(f).sequence
can be written:
z traverse f
- --
Tony Morris
http://tmorris.net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk1e5r8ACgkQmnpgrYe6r62i6ACffLCv/Itchec3SWqYJAMoy7Z4
b2QAn10vGRNj6KBeHYhqbPRrmMg2cGvr
=e4Yx
-----END PGP SIGNATURE-----