I'm learning Scala's generic content, and I don't know the meaning of literal in the code
sealed trait Natural
sealed trait Vect[N <: Natural, +A]:
def length: Int
def map[B](f: A => B): Vect[N, B]
def zip[B](that: Vect[N, B]): Vect[N, (A, B)]
def concat[M <: Natural, B >: A](that: Vect[M, B]): Vect[Plus[N, M], B]
What does Vect[N, (A, B)] mean, especially (A, B)?
A Vect is a type with 2 type parameters. The 1st must be some form of (sub-type of) Natural, we'll call it N. The 2nd is some unrestricted type defined at the call-site, we'll call it A.
The zip() method receives a different Vect. It must have the same 1st-parameter type (not just any Natural, it has to be the same) but the 2nd-parameter type might be different, we'll call it B. (It might be the same as A but it might not so it needs a different identifier.)
The zip() method returns a new Vect with the same 1st-parameter type but the 2nd type parameter is a 2-element tuple (a 2-ple) with the 1st element of type A and the 2nd element of type B.
The A and B have been "zipped" together.
(A,B) is type for tuple.
scala api has definition for zip, which is similar.
ref: https://docs.scala-lang.org/tour/tuples.html
Related
Does sum type have short denotation in Scala?
For example:
Product type -(Integer, Double)
Funtion type -(Integer=>Double)
Sum type -?
No, there is no syntactic sugar for defining sum types in Scala. In fact, Scala does not have sum types in its type system as a distinct feature. They need to be encoded somehow, the most widely-used encoding is as closed subtyping hierarchies, i.e. the sum type
A = B + C
is encoded as
sealed trait A
final class B extends A
final class C extends A
Note that Scala also does not have syntactic sugar for product types either. The syntax you mentioned is for tuple types, not product types. Tuple types are merely one of many different classes of product types. E.g. case classes are another class of product types.
Note also that Scala does not have a notion of product types in its type system either. They are encoded as parametric types that are subtypes of one of the ProductN types. I.e. the tuple type (A, B) is actually syntactic sugar for
Tuple2[A, B]
which is defined as
final case class Tuple2[+A, +B](_1: A, _2: B) extends Product2[A, B] with Product with Serializable
Of course, classes and traits can be seen as variants of records, so in some sense, every type in Scala can be seen as a product type, which is why it is so easy to encode them.
In Scala, the sum type constructor is given by scala.util.Either. If you need tagged sum of more than two types (e.g. A, B, C), then it usually makes sense to create a custom sealed trait:
sealed trait AorBorC
case class CaseA(a: A) extends AorBorC
case class CaseB(b: B) extends AorBorC
case class CaseC(c: C) extends AorBorC
and then work with pattern matching.
You can of course define shortcuts, if you want:
type +[A, B] = Either[A, B]
type \/[A, B] = Either[A, B]
and then use them in infix notation A + B or A \/ B.
You can also use \/ from ScalaZ.
The util.Either is indeed the coproduct as explained here:
Formation: for any two types A, B, you can form the type Either[A, B] (there are no restrictions)
Introduction: if a: A and b: B, then both Left(a) and Right(b) are values of type Either[A, B].
Elimination: if e: Either[A, B], then the usual pattern matching can be used to collapse both cases to a single type C:
e match {
case Left(a) => c1(a) // assuming `c1(a): C`
case Right(b) => c2(b) // assuming `c2(b): C`
}
There is also fold:
e.fold(c1, c2)
which does essentially the same, and also produces a value of type C. The meaning should be clear:
Left(a).fold(c1, c2) == c1(a)
Right(b).fold(c1, c2) == c2(b)
This is indeed the sum type of A and B, because for any other type C and any two functions c1: A => C, c2: B => C there is the function (e: Either[A, B]) => e.fold(c1, c2), for which the above two identities hold, which is exactly the universal property required by the definition of a coproduct.
I have a question that's been bugging me.
Lists in Scala are covariant (List[+A])
Let's say we have these classes:
class A
class B extends A
The map function of List[B] takes a function f: B => C
But I can also use a f: A => C
which is a subclass of f: B => C
and it totally makes sense.
What I am currently confused by is that
the map function should accept only functions that are superclasses of the original function (since functions are contravariant on their arguments), which does not apply in the example I've given.
I know there's something wrong with my logic and I would like to enlightened.
Your error lies in the assumption that map(f: A => C) should only accept functions that are superclasses of A => C.
While in reality, map will accept any function that is a subclass of A => C.
In Scala, a function parameter can always be a subclass of the required type.
The covariance of A in List[A] only tells you that, wherever a List[A] is required, you can provide a List[B], as long as B <: A.
Or, in simpler words: List[B] can be treated as if it was a subclass of List[A].
I have compiled a small example to explain these two behaviours:
class A
class B extends A
// this means: B <: A
val listA: List[A] = List()
val listB: List[B] = List()
// Example 1: List[B] <: List[A]
// Note: Here the List[+T] is our parameter! (Covariance!)
def printListA(list: List[A]): Unit = println(list)
printListA(listA)
printListA(listB)
// Example 2: Function[A, _] <: Function[B, _]
// Note: Here a Function1[-T, +R] is our parameter (Contravariance!)
class C
def fooA(a: A): C = ???
def fooB(b: B): C = ???
listB.map(fooB)
listB.map(fooA)
Try it out!
I hope this helps.
As you already suspected, you are mixing up things here.
On the one hand, you have a List[+A], which tells us something about the relationships between List[A] and List[B], given a relationship between A and B. The fact that List is covariant in A just means that List[B] <: List[A] when B <: A, as you know already know.
On the other hand, List exposes a method map to change its "contents". This method does not really care about List[A], but only about As, so the variance of List is irrelevant here. The bit that is confusing you here is that there is indeed some sub-typing to take into consideration: map accepts an argument (a A => C in this case, but it's not really relevant) and, as usual with methods and functions, you can always substitute its argument with anything that is a subtype of it. In your specific case, any AcceptedType will be ok, as long as AcceptedType <: Function1[A,C]. The variance that matters here is Function's, not List's.
I just found this function in the scala library (in the PartiallyOrdered trait):
def tryCompareTo [B >: A <% PartiallyOrdered[B]](that: B): Option[Int]
I wonder how should I exactly interpret the type bound. According to my research the >: operator is a lower type bound, and the <% is a view bound, (What are Scala context and view bounds?). However, when the two operators are combined, how do I read it.
Is it type B must be a super class of A, and A can be viewed as PartiallyOrdered[B]? (B >: (A <% PartiallyOrdered[B]), more or less grouped like this)
Or, type B must be a super class of A, and B can be viewed as PartiallyOrdered[B]? ((B >: A) <% PartiallyOrdered[B], more or less grouped like this).
An extra curiosity, can there be more than two operators combined?
In a type parameter list the first name in every comma delimited part is the type parameter that is being defined. All operators that follow constrain that type parameter. So the correct interpretation is type B must be a supertype of A, and B can be viewed as PartiallyOrdered[B].
After searching a bit I found in the scaladoc at https://www.scala-lang.org/api/current/scala/math/PartiallyOrdered.html the same function is written as
abstract def tryCompareTo[B >: A](that: B)(implicit arg0: (B) ⇒ PartiallyOrdered[B]): Option[Int]
The implicit parameter corresponds to the implementation of B <% PartiallyOrdered[B] according to What are Scala context and view bounds?, thus
B can be viewed as a PartiallyOrdered[B].
A can be viewed as PartiallyOrdered[B].
tryCompareTo is part of a bigger structure:
trait PartiallyOrdered[+A]
A is the type we are talking about here
I am trying to get a better understanding of the following behaviour:
scala> class C[-A, +B <: A]
<console>:7: error: contravariant type A occurs in covariant position
in type >: Nothing <: A of type B
class C[-A, +B <: A]
^
However the following works:
scala> class C[-A, +B <% A]
defined class C
I can see that there could be issues from the variance of the bounding and bounded variables being opposite, though I have am not clear on what the specific problem is.
I am even less clear on why changing the type bound to a view bound makes things ok. In the absence of applicable implicit conversions I would expect the two definitions to be have largely the same effect. If anything I would expect a view bound to provide more opportunities for mischief.
For a bit of background I defining classes that are in some ways like functions, and I wanted to do something like
CompositeFunc[-A, +B <: C, -C, +D] (f1 : BaseFunc[A, B], f2 : BaseFunc[C, D])
extends BaseFunc[A, D]
Arguably
CompositeFunc[-A, +B <% C, -C, +D] (f1 : BaseFunc[A, B], f2 : BaseFunc[C, D])
extends BaseFunc[A, D]
is actually preferable, but I would still like to better understand what is going on here.
First the easy one:
class C[-A, +B <% A]
This is equivalent to
class C[-A, +B](implicit view: B => A)
Since view is not publically returned, it is not in a position which would constrain the variance of A or B. E.g.
class C[-A, +B](val view: B => A) // error: B in contravariant position in view
In other words, C[-A, +B <% A] is no different than C[-A, +B] in terms of constraints, the view argument doesn't change anything.
The upper bound case C[-A, +B <: A] I'm not sure about. The Scala Language Specification in §4.5 states
The variance position of the lower bound of a type declaration or type parameter is the opposite of the variance position of the type declaration or parameter.
The variance of B does not seem to be involved, but generally the upper bound must be covariant:
trait C[-A, B <: A] // contravariant type A occurs in covariant position
This must somehow produce a problem? But I couldn't come up with an example which proofs that this construction becomes unsound in a particular case....
As for the composed function, why not just
class Composite[-A, B, +C](g: A => B, h: B => C) extends (A => C) {
def apply(a: A) = h(g(a))
}
EDIT: For example:
import collection.LinearSeq
def compose[A](g: Traversable[A] => IndexedSeq[A], h: Traversable[A] => LinearSeq[A]) =
new Composite(g, h)
Following watching Nick Partidge's presentation on deriving scalaz, I got to looking at this example, which is just awesome:
import scalaz._
import Scalaz._
def even(x: Int) : Validation[NonEmptyList[String], Int]
= if (x % 2 ==0) x.success else "not even: %d".format(x).wrapNel.fail
println( even(3) <|*|> even(5) ) //prints: Failure(NonEmptyList(not even: 3, not even: 5))
I was trying to understand what the <|*|> method was doing, here is the source code:
def <|*|>[B](b: M[B])(implicit t: Functor[M], a: Apply[M]): M[(A, B)]
= <**>(b, (_: A, _: B))
OK, that is fairly confusing (!) - but it references the <**> method, which is declared thus:
def <**>[B, C](b: M[B], z: (A, B) => C)(implicit t: Functor[M], a: Apply[M]): M[C]
= a(t.fmap(value, z.curried), b)
So I have a few questions:
How come the method appears to take a higher-kinded type of one type parameter (M[B]) but can get passed a Validation (which has two type paremeters)?
The syntax (_: A, _: B) defines the function (A, B) => Pair[A,B] which the 2nd method expects: what is happening to the Tuple2/Pair in the failure case? There's no tuple in sight!
Type Constructors as Type Parameters
M is a type parameter to one of Scalaz's main pimps, MA, that represents the Type Constructor (aka Higher Kinded Type) of the pimped value. This type constructor is used to look up the appropriate instances of Functor and Apply, which are implicit requirements to the method <**>.
trait MA[M[_], A] {
val value: M[A]
def <**>[B, C](b: M[B], z: (A, B) => C)(implicit t: Functor[M], a: Apply[M]): M[C] = ...
}
What is a Type Constructor?
From the Scala Language Reference:
We distinguish between first-order
types and type constructors, which
take type parameters and yield types.
A subset of first-order types called
value types represents sets of
(first-class) values. Value types are
either concrete or abstract. Every
concrete value type can be represented
as a class type, i.e. a type
designator (§3.2.3) that refers to a
class1 (§5.3), or as a compound type
(§3.2.7) representing an intersection
of types, possibly with a refinement
(§3.2.7) that further constrains the
types of itsmembers. Abstract value
types are introduced by type
parameters (§4.4) and abstract type
bindings (§4.3). Parentheses in types
are used for grouping. We assume that
objects and packages also implicitly
define a class (of the same name as
the object or package, but
inaccessible to user programs).
Non-value types capture properties of
identifiers that are not values
(§3.3). For example, a type
constructor (§3.3.3) does not directly
specify the type of values. However,
when a type constructor is applied to
the correct type arguments, it yields
a first-order type, which may be a
value type. Non-value types are
expressed indirectly in Scala. E.g., a
method type is described by writing
down a method signature, which in
itself is not a real type, although it
gives rise to a corresponding function
type (§3.3.1). Type constructors are
another example, as one can write type
Swap[m[_, _], a,b] = m[b, a], but
there is no syntax to write the
corresponding anonymous type function
directly.
List is a type constructor. You can apply the type Int to get a Value Type, List[Int], which can classify a value. Other type constructors take more than one parameter.
The trait scalaz.MA requires that it's first type parameter must be a type constructor that takes a single type to return a value type, with the syntax trait MA[M[_], A] {}. The type parameter definition describes the shape of the type constructor, which is referred to as its Kind. List is said to have the kind '* -> *.
Partial Application of Types
But how can MA wrap a values of type Validation[X, Y]? The type Validation has a kind (* *) -> *, and could only be passed as a type argument to a type parameter declared like M[_, _].
This implicit conversion in object Scalaz converts a value of type Validation[X, Y] to a MA:
object Scalaz {
implicit def ValidationMA[A, E](v: Validation[E, A]): MA[PartialApply1Of2[Validation, E]#Apply, A] = ma[PartialApply1Of2[Validation, E]#Apply, A](v)
}
Which in turn uses a trick with a type alias in PartialApply1Of2 to partially apply the type constructor Validation, fixing the type of the errors, but leaving the type of the success unapplied.
PartialApply1Of2[Validation, E]#Apply would be better written as [X] => Validation[E, X]. I recently proposed to add such a syntax to Scala, it might happen in 2.9.
Think of this as a type level equivalent of this:
def validation[A, B](a: A, b: B) = ...
def partialApply1Of2[A, B C](f: (A, B) => C, a: A): (B => C) = (b: B) => f(a, b)
This lets you combine Validation[String, Int] with a Validation[String, Boolean], because the both share the type constructor [A] Validation[String, A].
Applicative Functors
<**> demands the the type constructor M must have associated instances of Apply and Functor. This constitutes an Applicative Functor, which, like a Monad, is a way to structure a computation through some effect. In this case the effect is that the sub-computations can fail (and when they do, we accumulate the failures).
The container Validation[NonEmptyList[String], A] can wrap a pure value of type A in this 'effect'. The <**> operator takes two effectful values, and a pure function, and combines them with the Applicative Functor instance for that container.
Here's how it works for the Option applicative functor. The 'effect' here is the possibility of failure.
val os: Option[String] = Some("a")
val oi: Option[Int] = Some(2)
val result1 = (os <**> oi) { (s: String, i: Int) => s * i }
assert(result1 == Some("aa"))
val result2 = (os <**> (None: Option[Int])) { (s: String, i: Int) => s * i }
assert(result2 == None)
In both cases, there is a pure function of type (String, Int) => String, being applied to effectful arguments. Notice that the result is wrapped in the same effect (or container, if you like), as the arguments.
You can use the same pattern across a multitude of containers that have an associated Applicative Functor. All Monads are automatically Applicative Functors, but there are even more, like ZipStream.
Option and [A]Validation[X, A] are both Monads, so you could also used Bind (aka flatMap):
val result3 = oi flatMap { i => os map { s => s * i } }
val result4 = for {i <- oi; s <- os} yield s * i
Tupling with `<|**|>`
<|**|> is really similar to <**>, but it provides the pure function for you to simply build a Tuple2 from the results. (_: A, _ B) is a shorthand for (a: A, b: B) => Tuple2(a, b)
And beyond
Here's our bundled examples for Applicative and Validation. I used a slightly different syntax to use the Applicative Functor, (fa ⊛ fb ⊛ fc ⊛ fd) {(a, b, c, d) => .... }
UPDATE: But what happens in the Failure Case?
what is happening to the Tuple2/Pair in the failure case?
If any of the sub-computations fails, the provided function is never run. It only is run if all sub-computations (in this case, the two arguments passed to <**>) are successful. If so, it combines these into a Success. Where is this logic? This defines the Apply instance for [A] Validation[X, A]. We require that the type X must have a Semigroup avaiable, which is the strategy for combining the individual errors, each of type X, into an aggregated error of the same type. If you choose String as your error type, the Semigroup[String] concatenates the strings; if you choose NonEmptyList[String], the error(s) from each step are concatenated into a longer NonEmptyList of errors. This concatenation happens below when two Failures are combined, using the ⊹ operator (which expands with implicits to, for example, Scalaz.IdentityTo(e1).⊹(e2)(Semigroup.NonEmptyListSemigroup(Semigroup.StringSemigroup)).
implicit def ValidationApply[X: Semigroup]: Apply[PartialApply1Of2[Validation, X]#Apply] = new Apply[PartialApply1Of2[Validation, X]#Apply] {
def apply[A, B](f: Validation[X, A => B], a: Validation[X, A]) = (f, a) match {
case (Success(f), Success(a)) => success(f(a))
case (Success(_), Failure(e)) => failure(e)
case (Failure(e), Success(_)) => failure(e)
case (Failure(e1), Failure(e2)) => failure(e1 ⊹ e2)
}
}
Monad or Applicative, how shall I choose?
Still reading? (Yes. Ed)
I've shown that sub-computations based on Option or [A] Validation[E, A] can be combined with either Apply or with Bind. When would you choose one over the other?
When you use Apply, the structure of the computation is fixed. All sub-computations will be executed; the results of one can't influence the the others. Only the 'pure' function has an overview of what happened. Monadic computations, on the other hand, allow the first sub-computation to influence the later ones.
If we used a Monadic validation structure, the first failure would short-circuit the entire validation, as there would be no Success value to feed into the subsequent validation. However, we are happy for the sub-validations to be independent, so we can combine them through the Applicative, and collect all the failures we encounter. The weakness of Applicative Functors has become a strength!