On Fri, Jan 3, 2014 at 4:42 PM, Bill Venners <bi...@artima.com> wrote:
> Hi All,
>
> Tony Morris suggested we take a discussion off Twitter. We decided to move
> it here because it involves Scalaz's \/, Validation, ValidationNEL, for
> which I believe Tony is the primary author, and ScalaUtils' Or, for which I
> am the author. Essentially I have some specific questions for Tony to help
> me understand why \/ and Valdation are designed the way they are, and I
> wanted to explain why I designed Or the way I did. Twitter is not the right
> forum for that. So here goes.
>
> One difference between \/ and Or is that \/ is right-biased whereas Or is
> left-biased. My first question is why is right bias the tradition? \/ and
> Validation are right-biased outright; Either suggests putting "good" results
> on the right, error results on the left. The consistency is good, but where
> did it come from in the first place?
It because it is how the type parameters should be partially applied.
In scala-like terms you want the functor to be:
Either[A, _]
not
Either[_, B].
> The reason I made Or left-biased, which is inconsistent with tradition, is
> that I think it reads better textually to have the "valid" result type first
> when types are written in infix notation:
>
> def parseName(input: String): String Or ErrorMessage = ???
I don't think discussing it in terms of syntax is useful, I will just
say I strongly disagree with the above being a positive.
> The other main question I have is why are there two types, \/ and
> Validation? ScalaUtils just has one type, Or, which can be used to
> accumulate errors if the "bad" type is an Every (which like NonEmptyList, is
> a sequence containing one to many elements). Having one type is is think
> simpler for users. It avoids user confusion about which one to use, such as
> Travis Brown's question here:
>
> http://stackoverflow.com/questions/20065853/validation-versus-disjunction/20134523
>
> I imagine there's a good reason to have both \/ and Validation, and I'd like
> to understand what it is. My guess is it has to do with the reason flatMap
> is deprecated in Validation, something to do with a conflict between monad
> and applicative laws making it impossible to be both at once. If so I may
> need some hand-holding as I don't feel I understand applicatives very well
> yet, but I'd like to understand from the applicative perspective why I was
> able to do this in one type. Does the Or/Every combination perhaps violate
> an applicative law, something you would not want to do, and that's why I was
> able to do this in one type?
If you put both behaviours in one type any code that depends on the
abstract concepts (applicative/monad) is not predictable. Will this
function accumulate errors or will it not?
The reason both exist is simply because sometimes you want the non
accumulating version and sometimes you want the accumulating version.
This is dependent on the context not the types, i.e. just because the
left is a list doesn't mean you want to accumulate. You should be
explicit, I want this behaviour, so I want this type.
I would suggest that there are either edge conditions which are
confusing/non-predictable or there is actually no code exploiting the
more general concepts with respect to Or/Every.
Validation is scalaz is almost always used incorrectly because of the
existence of the flatMap method. I have seen very few uses of
validation that I would not consider a bug - they do exist - but
people leap to it because of the pretty name which some what
ridiculous, and then they have a Nel that always has one and only one
value in it.
It because it is how the type parameters should be partially applied.
In scala-like terms you want the functor to be:
Either[A, _]
not
Either[_, B].
Interesting, but I could use a bit more elaboration. Can you give me a specific example of why I might want to partially apply one of the two types in an Either? Which one of the two types would I normally know and want to "fill in" leaving a type that just has one type parameter. And what would I be trying to accomplish by that?
Validation is scalaz is almost always used incorrectly because of the
existence of the flatMap method. I have seen very few uses of
validation that I would not consider a bug - they do exist - but
people leap to it because of the pretty name which some what
ridiculous, and then they have a Nel that always has one and only one
value in it.
I see. you mean they use Validation when they don't want to accumulate errors, and because it has flatMap they can make pretty for comprehensions with Validation that short circuit at the first sign of trouble? That's interesting. You can do the same thing with an accumulating Or, but I don't see that as a bug. It is just one way to use an Or. Accumulating or not, if you use an Or in a for-expression, it will short circuit at the first failure.
On Friday, January 3, 2014, Bill Venners wrote:
It because it is how the type parameters should be partially applied.
In scala-like terms you want the functor to be:
Either[A, _]
not
Either[_, B].
Interesting, but I could use a bit more elaboration. Can you give me a specific example of why I might want to partially apply one of the two types in an Either? Which one of the two types would I normally know and want to "fill in" leaving a type that just has one type parameter. And what would I be trying to accomplish by that?The most basic case is `trait Functor[F[_]] { def map[A, B](a: F[A])(f: A => B): F[B] }` for dealing generally with things that have map.In non-scala languages that have higher kinds, there are elegant ways to partially apply type parameters, to translate into an imaginary scala-like syntax, to get a conforming 'F' in this example for Either, you would just write something like Either[A]. In scala we have to do type lambdas and things get horrible pretty quickly, but you can exploit the same ordering to get some help in scala. In fact scalaz 6 used to provide a large number of types for doing just that. So running with the example you would normally write something like:def EitherFunctor[A] = new Functor[[({type l[a] = Either[A, a]})#l] { ... }But by exploiting the ordering of the type arguments we can come up with general types to fit. In this case something like:class OneOfTwo[F[_, _], A] { type l[a] = F[A, a] }And use it (in a non convincing way because of the somewhat trivial nature of Either):def EitherFunctor[A] = new Functor[OneOfTwo[Either, A]#l] { ... }For just either this seems kind of neither here nor their, but as you build up larger monad transformer stacks you _really_ want there to be tools for dealing with these things rather than having to do the type lambda dance every time. So there is a benefit to the ordering even in scala (particularly with respect to consistency of other data structures like Reader, Writer and State, and it is worth nothing these all have there arguments the same order as Either in that the right most type parameter is the 'A' that you map across, and they have no concept of the "failure" case so the ordering of either based on failure the success is not an accurate reflection, it is simply about partially applying for Functor).
Validation is scalaz is almost always used incorrectly because of the
existence of the flatMap method. I have seen very few uses of
validation that I would not consider a bug - they do exist - but
people leap to it because of the pretty name which some what
ridiculous, and then they have a Nel that always has one and only one
value in it.
I see. you mean they use Validation when they don't want to accumulate errors, and because it has flatMap they can make pretty for comprehensions with Validation that short circuit at the first sign of trouble? That's interesting. You can do the same thing with an accumulating Or, but I don't see that as a bug. It is just one way to use an Or. Accumulating or not, if you use an Or in a for-expression, it will short circuit at the first failure.
I feel it is a bug because they will have to handle non-possible cases at the end. For example given a Validation[List[String], Int], if i can only ever have one error, I still have to handle the empty list case and the more than one case, what should I do, throw an exception? promise it won't happen?
Scalaz \/ and Validation are designed to work together, not necessarily one or the other. There are operations on both for switching between and back, and work has been done to ensure the libraries are almost identical, so in the situation you want to accumulate you can switch to validation and then back. This makes any behaviour change explicit and recored in the type, so you get what you want every time (at least after flatMap is removed off of validation) and importantly you can work with abstract examples I was discussing above.
Hope that helps with understanding the rationale.
can I provide a valid (law-abiding) Applicative instance for Or[G, Every[B]]
It seems like you could, but since every monad is also an Applicative, it might be difficult for Scala to infer which you want, the Or monad or the Or.Every Applicative.
--
You received this message because you are subscribed to the Google Groups "scalaz" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalaz+un...@googlegroups.com.
To post to this group, send email to sca...@googlegroups.com.
Visit this group at http://groups.google.com/group/scalaz.
For more options, visit https://groups.google.com/groups/opt_out.
--
can I provide a valid (law-abiding) Applicative instance for Or[G, Every[B]]It seems like you could, but since every monad is also an Applicative, it might be difficult for Scala to infer which you want, the Or monad or the Or.Every Applicative.
> and that's why I was able to do this in one type?It's absolutely not true that you were able to do this with one type. You have two types:_ Or EAnd..._ Or Every[E]The composition of Or and Every is not the same type as Or by itself.
implicit def ValidationApplicative[L: Semigroup]: Applicative[({type l[a] = Validation[L, a]})#l]
> I would like to better understand the relationship between Monad and
> Applicative. The answer to my original question about why \/ and Validation
> are two different types in scalaz seems to be that the Applicative provided
> with Monad is different from the Applicative that accumulates. So an
> Applicative that accumulates can't be paired with Monad. Can someone
> elaborate on exactly what that is, and why? Is the Applicative that comes
> out of the box with Monad required by the math principles, or just provided
> by scalaz because it is usually convenient.
For each Monad, there is an implementation of Applicative which follows
from the implementation of `bind` and `return`. The relevant code in
scalaz is this:
override def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] = {
lazy val fa0 = fa
bind(f)(map(fa0))
}
This default implementation gives rise to a lawful Applicative.
(More generally speaking, a valid Bind gives rise to a valid Apply, and
`Bind.scala` is in fact the place where this snippet appears in the
scalaz sources.)
Now, in the `Monad[Validation[E, _]]` case, there is an Applicative
which accumulates errors. Additionally, there can be no Monad which
accumulates errors (try to write it � it's impossible). On the other
hand, there is a Monad with fail-fast behaviour. Now, that Monad gives
rise to an Applicative instance (via the default implementation given
above), but it's an entirely different one.
Now, the question is, why does a Monad give rise to an Applicative?
Well, because you can implement a law-abiding `ap` based on a
law-abiding `bind`. It's as simple as that; one could even say that it's
inevitable.
Now, in the `Monad[Validation[E, _]]` case, there is an Applicative
which accumulates errors. Additionally, there can be no Monad which
accumulates errors (try to write it � it's impossible). On the other
hand, there is a Monad with fail-fast behaviour. Now, that Monad gives
rise to an Applicative instance (via the default implementation given
above), but it's an entirely different one.
Now, the question is, why does a Monad give rise to an Applicative?
Well, because you can implement a law-abiding `ap` based on a
law-abiding `bind`. It's as simple as that; one could even say that it's
inevitable.
But I guess my question could be phrased: is it *inevitable* that every monad will be associated with an Applicative that implements ap as the reasonable default provided in Bind.scala. From what I've seen so far, nothing about monad-hood requires that. Moreover I've observed scalaz users worried about flatMap going away in Validation. They want to use Validation in a monadic way in for expressions, even though it short circuits when they use it that way. Could you not go further and offer a Monad[Validation...] that overrides ap (using the wonders of OO and subtyping) to do accumulation? Wouldn't that make Validation even more generally useful?
That's essentially how accumulating Or's work. You can use, say, an Int Or Every[ErrorMessage] in a for expression, and it will short circuit in the monad style, which I think is OK because they are using its monad-nature when using it in a for expression. If they want error accumuilation, they use a different set of methods than the ones to which for expressions are desugared.
But I guess my question could be phrased: is it *inevitable* that every monad will be associated with an Applicative that implements ap as the reasonable default provided in Bind.scala. From what I've seen so far, nothing about monad-hood requires that.
But I guess my question could be phrased: is it *inevitable* that every monad will be associated with an Applicative that implements ap as the reasonable default provided in Bind.scala. From what I've seen so far, nothing about monad-hood requires that. Moreover I've observed scalaz users worried about flatMap going away in Validation. They want to use Validation in a monadic way in for expressions, even though it short circuits when they use it that way. Could you not go further and offer a Monad[Validation...] that overrides ap (using the wonders of OO and subtyping) to do accumulation? Wouldn't that make Validation even more generally useful?
But I guess my question could be phrased: is it *inevitable* that every monad will be associated with an Applicative that implements ap as the reasonable default provided in Bind.scala. From what I've seen so far, nothing about monad-hood requires that.
You could certainly implement `ap` in a way that is inconsistent with the monad. But then what about `liftM2`, `liftM3`, etc? Which behavior should they exhibit? You can special-case everything, but it's inconsistent and confusing.
But I guess my question could be phrased: is it *inevitable* that every monad will be associated with an Applicative that implements ap as the reasonable default provided in Bind.scala. From what I've seen so far, nothing about monad-hood requires that.
You could certainly implement `ap` in a way that is inconsistent with the monad. But then what about `liftM2`, `liftM3`, etc? Which behavior should they exhibit? You can special-case everything, but it's inconsistent and confusing. Why not have one type for one behavior and a different (but isomorphic) type for the other? Is there a type famine that I'm not aware of?
Actually in my question about Validation I wasn't suggesting you could merge them into one type, just trying to understand why some of you think Validation shouldn't also be a monad. It sounds to me like the answer is that even though it wouldn't strictly violate any law of monadic nature, it would be inconsistent with other monads provided by scalaz and therefore possibly surprising to users in a way that leads to bugs. Is that accurate?
No type famine. I just want to make sure I understand the rationale.
Where are liftM2 and liftM3 defined? I couldn't find any such names in the 7.0.5 codebase.
If you see these signatures and F[_] = Validation[E, _], then you can reasonably expect that the errors are accumulated. If F[_] = E \/ _, then you know for sure that they are not (because it's not possible).
--
You received this message because you are subscribed to the Google Groups "scalaz" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalaz+un...@googlegroups.com.
To post to this group, send email to sca...@googlegroups.com.
Visit this group at http://groups.google.com/group/scalaz.
For more options, visit https://groups.google.com/groups/opt_out.
The reason you might not want to do that is it may be more confusing than any
benefit it provides. It might be simpler for user if there's one type, \/, that
is a monad with consistent applicative behavior and similar type Validation that
is not a monad (including not having a flatMap method so it can't be used monadically
in a for expression) and has an applicative that accumulates.
On Sunday, January 5, 2014 2:02:03 PM UTC-7, Bill Venners wrote:
The reason you might not want to do that is it may be more confusing than any
benefit it provides. It might be simpler for user if there's one type, \/, that
is a monad with consistent applicative behavior and similar type Validation that
is not a monad (including not having a flatMap method so it can't be used monadically
in a for expression) and has an applicative that accumulates.I dislike the argument that having one type with two different behaviors is less confusing that two types with two different (but consistent) behaviors. The fundamental problem is that the behavior of my code can change dramatically if I accidentally change the error type to something that has a semigroup - namely, I go from abort-on-first-error semantics to abort-if-one-fails semantics. This kind of hidden information (not evident at the call site, because all the relevant typeclasses are fetched implicitly) is really dangerous.I'd like to try to convince you that having one type represent two behaviors is insidious with a more obvious example. Consider strings.
Hi Kris,
On Monday, January 6, 2014 1:19:29 PM UTC-8, Kris Nuttycombe wrote:
On Sunday, January 5, 2014 2:02:03 PM UTC-7, Bill Venners wrote:
The reason you might not want to do that is it may be more confusing than any
benefit it provides. It might be simpler for user if there's one type, \/, that
is a monad with consistent applicative behavior and similar type Validation that
is not a monad (including not having a flatMap method so it can't be used monadically
in a for expression) and has an applicative that accumulates.I dislike the argument that having one type with two different behaviors is less confusing that two types with two different (but consistent) behaviors. The fundamental problem is that the behavior of my code can change dramatically if I accidentally change the error type to something that has a semigroup - namely, I go from abort-on-first-error semantics to abort-if-one-fails semantics. This kind of hidden information (not evident at the call site, because all the relevant typeclasses are fetched implicitly) is really dangerous.I'd like to try to convince you that having one type represent two behaviors is insidious with a more obvious example. Consider strings.Actually my opinion was already leaning in that direction, so I'll be easy to convince. My example was interesting to work out, but the end result is a rather slippery Validation type, which could transform itself to fit its context in ways that may surprise its user. I do see that.
I posted here originally because wanted to understand why the design of scalaz's \/ and Validation came out differently than scalautils' Or. I expected there were good reasons, and I think I have figured out what those reasons are. Hopefully I won't ignite a z-explosion, but I'd like to post my conclusion to see what you all think.
I believe the main difference that drives the scalaz and scalautils designs for these concepts in different directions is that scalaz wants to use Applicative to accumulate errors, and scalautils does not. And the issue for scalaz is that, for example, an Applicative[Validation[List[Int], String]]'s ap method could either accumulate errors or not. Both accumulation and non-accumulation are valid implementations of the Applicative.ap method. Thus I believe Applicative[Validation[List[Int], String]] is an example of, to quote Kris, "having one type represent two behaviors."
Now, I believe that's the point of Applicative. It is an abstraction defined by methods of a certain shape that obey certain laws, which allows many different concepts to be represented and treated uniformly. It allows you to write generic code for those different concepts, reducing code duplication, and in ways that still I'd like to understand better, helps you reason about that code. But because Applicative.ap can mean two different things in this case, you want to have two different types, \/ and Validation, to make it more obvious which Applicative behavior is going to happen.
Or doesn't have that same pull to its design, because it doesn't use Applicative. I also, like you, want it to be obvious to readers and writers of code whether an Or is accumulating or short-circuiting. And it is clear, even though I just use one type.
Or by itself just behaves like a monad, very much like \/. You'd can access its monad-hood through for expressions, for example. And it always works the same. It's flatMap methods can be chained and will short circuit at the first sign of trouble. Always. No matter what. Even if the bad type is an Every, which makes it an "accumulating Or", flatMap will still short circuit.
If the bad type is an Every, though, you can import the members of object Accumulation, and a *different* API will become available (though implicits) that allows you to ask that Or to accumulate errors. If you use that accumulation API and it compiles, it will accumulate, always. If you try and use the accumulating API on an Or that doesn't have an Every bad type, then it just won't compile because it can't accumulate. So if and only if it compiles, the accumulating API will accumulate, always.
In short, I think the underlying reason the scalautils design works on one type is because I don't have any method like ap that can mean two different things. The reason the scalaz design works better with two types, is you do have that ap method to contend with.
Bill
Kris
--
--
The recommended way is to use different types for different monoids.e.g.Monoid[Int @@ Multiplication]Monoid[Int @@ Addition]Also, you keep insisting that you're using only one type `Or` for both of your "personalities". That's simply not true.
Lastly, I tried using type tags, because I thought it might be nice to just turn accumulation on with an @@ Acc annotation. I didn't quite get it to work yet, but already had to do lots of casting. Seems the compiler doesn't infer phantom-type-hood.