Problem using sequence: ambiguous implicit values

167 views
Skip to first unread message

Alexey Zlobin

unread,
Feb 17, 2011, 2:05:27 PM2/17/11
to sca...@googlegroups.com
Hi,

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.

Jason Zaugg

unread,
Feb 17, 2011, 5:10:51 PM2/17/11
to sca...@googlegroups.com, Alexey Zlobin
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]

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

Alexey Zlobin

unread,
Feb 18, 2011, 11:56:34 AM2/18/11
to Jason Zaugg, sca...@googlegroups.com
Thank you for such detailed answer. See comments inline.

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

Jason Zaugg

unread,
Feb 18, 2011, 12:06:46 PM2/18/11
to Alexey Zlobin, sca...@googlegroups.com
On Fri, Feb 18, 2011 at 5:56 PM, Alexey Zlobin <alexey...@gmail.com> wrote:
> 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?

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!)

Tony Morris

unread,
Feb 18, 2011, 4:38:07 PM2/18/11
to sca...@googlegroups.com

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

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-----

Reply all
Reply to author
Forward
0 new messages