Can this be simplified? - scala

Referring to a previous answer of mine on stackoverflow
The core of the complexity is illustrated in just one method:
implicit def traversableToFilterOps[CC[X] <: Traversable[X], T]
(xs: CC[T])(implicit witness: CC[T] <:< TraversableLike[T,CC[T]]) =
new MoreFilterOperations[CC[T], T](xs)
With two questions:
Is there any way to give the compiler a hint that Map meets the signature CC[X] <: Traversable[X]?
I would expect it to match as a Traversable[Tuple2[_,_]] but this doesn't happen. In the end, I had to write a second method taking CC[KX,VX] <: Map[KX,VX], but that feels redundant
witness: CC[T] <:< TraversableLike[T,CC[T]] also seems redundant given the first type parameter, my gut feeling is that this is enforced by the type of Traversable and must always hold true for any possible subclass or value of X, so there should be no reason to explicitly require it as a witness.
If I test this using an existential type in the REPL, then the compiler seems to agree with me:
scala> implicitly[Traversable[X] <:< TraversableLike[X,Traversable[X]] forSome { type X }]
res8: <:<[Traversable[X],scala.collection.TraversableLike[X,Traversable[X]]] forSome { type X } = <function1>
Is there therefore any way to do away with the boilerplate?

I'm a Scala noob, so please don't shoot me down if this doesn't help.
Assuming this:
class MoreFilterOperations[Repr <% TraversableLike[T,Repr], T] (xs: Repr) {}
Would something like this work?
// t2fo is short for traversableToFilterOps
implicit def t2fo[Repr <% TraversableLike[T, Repr], T](xs: Repr) =
new MoreFilterOperations[Repr, T](xs)
// m2fo is short for mapToFilterOps
implicit def m2fo[Repr <% Map[K, V] <% TraversableLike[(K,V), Repr], K, V]
(xs: Repr) = new MoreFilterOperations[Repr, (K, V)](xs)
This should work because (according to the book I have.. Programming Scala, p264) the following method definition with a view bound:
def m [A <% B](arglist): R = ...
It is effectively the same as this method definition:
def m [A](arglist)(implicit viewAB: A => B): R = ...

Related

Shapeless: what the difference between these two approaches of instance derivation?

Can someone explain me what the difference between these two approaches for typeclass instance derivation (specifically for Option[A])?
1.
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
}
trait LowPriority extends LowestPriority {
final implicit def generic[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
// special instances for Options
trait LowestPriority {
implicit def genericOption[A, Repr <: HList](
implicit gen: Generic.Aux[A, Repr],
hEncoder: Lazy[MyTrait[Option[Repr]]]
): MyTrait[Option[A]] = ???
implicit val hnilOption: MyTrait[Option[HNil]] = ???
implicit def productOption1[H, T <: HList](
implicit
head: Lazy[MyTrait[Option[H]]],
tail: Lazy[MyTrait[Option[T]]],
notOption: H <:!< Option[Z] forSome { type Z }
): MyTrait[Option[H :: T]] = ???
implicit def product2[H, T <: HList](
implicit
head: Lazy[MyTrait[Option[H]]],
tail: Lazy[MyTrait[Option[T]]
): MyTrait[Option[Option[H] :: T]] = ???
}
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
}
trait LowPriority {
// deriving instances for options from existing non-option instances
final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ??? // <<<----
final implicit def generic[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
I tried both and they worked correctly, but i'm not sure that they will produce the same results for all cases (maybe i've missed something).
Do we really need LowestPriority instances for this?
Am i right if i would say that the first approach gives us just a little bit more flexibility?
I assuming that by "worked correctly" you mean "compiled" or "worked for some simple use case".
Both of your examples deal with generic product types, but not with generic sum types, so there is no risk that e.g. Option[A] could get derived using Some[A] :+: None :+: CNil, which would enforce some ambiguity. So (as far as I can tell) you could write the second version like:
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
// deriving instances for options from existing non-option instances
final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ???
}
trait LowPriority {
// <<<----
final implicit def hcons[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
and it would derive things correctly.
But how 1. and 2. differs?
In second version you can derive MyTrait[Option[A]] if you can derive for A, and you can derive for any A which is primitive/option/product - so Option[Option[String]], Option[String] and Option[SomeCaseClass] should all work. It should also work if this SomeCaseClass contains fields which are Options, or other case classes which are Options, etc.
Version 1. is slightly different:
at first you are looking for primitives
then you try to derive for a product (so e.g. Option would not be handled here)
then you do something weird:
genericOption assumes that you created a Option[Repr], and then I guess map it using Repr
in order to build that Repr you take Option[HNil] and prepend types inside Option using productOption, which would break if someone used Option as a field
so you "fix" that by prepending an Option in a special case product2
I guess, you tested that only against case classes, because the first version would not work for:
Option for primitives (Option[String], Option[Int] or whatever you defined as primitive)
nested options (Option[Option[String]])
options for custom defined types which are not case classes but have manually defined instances:
class MyCustomType
object MyCustomType {
implicit val myTrait: MyTrait[MyCustomType]
}
implicitly[Option[MyCustomType]]
For that reason any solution with implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] is simpler and more bulletproof.
Depending on what you put directly into companion low-priority implicits might be or might not be needed:
if you defined coproducts then manual support for e.g. Option, List, Either could conflict with shapeless derived ones
if you manually implemented MyTrait implicit for some type in its companion object then it would have the same priority as implicits directly in MyTrait - so if it could be derived using shapeless you could have conflicts
For that reason it makes sense to put shapeless implicits in LowPriorityImplicits but primitives, and manual codecs for List, Option, Either, etc directly in companion. That is, unless you defined some e.g. Option[String] implicits directly in companion which could clash with "Option[A] with implicit for A".
Since I don't know your exact use case I cannot tell for sure, but I would probably go with the seconds approach, or most likely with the one I implemented in the snippet above.
Actually it's hard to say without right hand sides and actual implementations.
From information you provided it doesn't follow that the two type classes behave equivalently.
For example in the 1st approach you consider some special cases, so theoretically it's possible that you redefine some general behavior in special case differently.
By the way, Option[A] is a coproduct of Some[A] and None.type (List[A] is a coproduct of scala.::[A] and Nil.type) and sometimes it's easier to derive a type class for coproducts than for Option[A] (or List[A]).

Scala implicit def returning A with B

I've been wracking my head against this and I can't figure out if there is a way to properly do this.
I feel I know what the problem is, but don't know how to solve it.
I have a method:
implicit def combineAlg[A: Alg, B: Alg]: Alg[A with B] = ...
if I call it explicitly it works fine, however it never gets implied properly.
// works
implicit val comb: Alg[A with B] = combineAlg[A, B]
// doesn't work
implicit val comb: Alg[A with B] = implicitly[Alg[A with B]]
Through my debugging with -Xlog-implicits, I believe its calling combineAlg[A with B, Nothing].
I'm looking to find a way to do something like:
implicit def combineExpAlg[AB, A >: AB, B >: AB]
or
implicit def combineExpAlg[AB, A, B](implicit ev1: AB <:< A, ev2: AB <:< B)
so that it understands that it needs to split apart the "with", but neither helps.
Not sure if there is a way to do this, really its an experiment I'm doing for "object algebras" in Scala and I'm trying to see how to remove boilerplate.
It'd be awesome if there was a solution.
A dotty solution would also be acceptable, as I'm also implementing it there to see if some of the new features make it simpler.
In case more information is needed you can view the repository here
What I'm trying to change is algebra.combineExpAlg.
It will look like it's working because I define specific implicits in algebra.interpreters.package that specifically spell out each interpreter pair, which is what I'm trying to generalize.
The following code compiles:
trait Alg[T]
trait LowPriorityAlg {
implicit def bAlg: Alg[B0] = ???
}
object Alg extends LowPriorityAlg {
implicit def aAlg: Alg[A0] = ???
implicit def combineAlg[AB, A: Alg, B: Alg](implicit ev1: AB <:< A, ev2: AB <:< B): Alg[AB] = ???
}
trait A0
trait B0
val comb: Alg[A0 with B0] = Alg.combineAlg[A0 with B0, A0, B0]
val comb1: Alg[A0 with B0] = implicitly[Alg[A0 with B0]]

Generically adding implicits to both TreeSet and TreeMaps

I want to add some helpful implicits to both mutable and immutable TreeMaps and TreeSets in Scala.
Here is my attempt:
First try to define the least upper bound of TreeMap and TreeSet that has headOption/lastOption (from GenTraversableLike) and from/to/until (from Sorted):
type SortedCollection[A, Repr <: SortedCollection[A, Repr]] = collection.generic.Sorted[A, Repr] with collection.GenTraversableLike[A, Repr]
Write my util:
implicit class RichSortedCollection[A, Repr <: SortedCollection[A, Repr]](s: SortedCollection[A, Repr]) {
def greaterThanOrEqualTo(a: A): Option[A] = s.from(a).headOption
def lessThan(a: A): Option[A] = s.until(a).lastOption
def lessThanOrEqualTo(a: A): Option[A] = s.to(a).lastOption
}
This only works partially: SortedSet#greaterThan compiles but TreeMap#greaterThan does not. How do I fix it?
TreeMap[A, B] (transitively) extends GenTraversableLike[(A, B), TreeMap[A, B]] and Sorted[A, TreeMap[A, B]], so you could say it's a:
Sorted[A, TreeMap[A, B]] with GenTraversableLike[(A, B), TreeMap[A, B]]
This is close to your type alias, but the first type parameter of Sorted and GenTraverableLike in the type alias SortedCollection must be the same, which they are not above. They simply aren't compatible. That is, Repr = TreeMap[A, B] is fine, but A = (A, B) doesn't make sense.
You're going to have the same issue with all map types, and your only real choice is to re-implement RichSortedCollection for maps as well.

Can an existentially quantified type variable be forced to have only a single type?

Consider the following code
trait Foo[T] {
def one: Foo[_ >: T]
def two: T
def three(x: T)
}
def test[T](f: Foo[T]) = {
val b = f.one
b.three(b.two)
}
The method test fails to type check. It says:
found : (some other)_$1(in value b)
required: _$1(in value b)
val x = b.three(b.two)
If I am interpreting this correctly, the compiler thinks that b in method test has a type that looks like this (not legal syntax, but hopefully clearer):
trait Foo {
def two: X where ∃ X >: T
def three(x: X where ∃ X >: T)
}
What I was hoping for was that it would have a type like this:
∃ X >: T such that trait Foo {
def two: X
def three(x: X)
}
The intent being that while the precise type X is not known, the compiler knows that its the same unknown type being returned by "two" and expected by "three". This seems different from what happens with normal univerally quantified generics. The following compiles, but exposes the type parameter X which I want to hide as it will vary between instances of Foo:
trait Foo[T] {
def one[X >: T]: Foo[X]
def two: T
def three(x: T)
}
def test[T, X >: T](f: Foo[T]) = {
val b = f.one[X]
b.three(b.two)
}
Is there a way to get the same behaviour for existentially quantified generics we get when they're univerally quanified?
def one: Foo[_ >: T] is equivalent to
def one: Foo[U >: T] forSome {type U >: T}
this one instead works
def one: Foo[U forSome {type U >: T}]
I do not however understand why this would make a difference. It seems like it should not to me. (shrug)
The problem is the compiler thinks that
b.two: _>:T
b.three(_>:T)
i.e. two is a supertype of T and three requires a supertype of T. But a supertype of T is not necessarily assignment compatible with another supertype of T, as in this example:
A >: B >: C
def get:A
def put(B)
put(get) // type mismatch
So if all the information we have is that they are supertypes of T then we cannot do this safely.
We have to explicitly tell the compiler that they are the same supertype of T.
trait Foo[T] {
type U <: T
def one: Foo[U]
def two: T
def three(x: T)
}
Then just set U when you implement the trait:
val x = new Foo[Dog]{
type U = Mammal
...
I would prefer this approach over the existential types due to the cleaner syntax and the fact that this is core Scala and does not need the feature to be imported.

View bound not compatible with upper type bound?

I have a method that takes a Comparable and returns a Comparable and wraps another method that does the same thing:
def myMethod[T <: Comparable[T]](arg: T): T = otherMethod(arg)
def otherMethod[T <: Comparable[T]](arg: T): T = arg
This compiles, but doesn't allow me to call myMethod with an Int or any other type that requires an implicit conversion to implement Comparable. As I understand it, view bounds are meant to address this type of problem, but using the view bound
def myMethod[T <% Comparable[T]](arg: T): T = otherMethod(arg)
I get the compiler error:
inferred type arguments [T] do not conform to method otherMethod's type parameter bounds [T <: java.lang.Comparable[T]]
So far, the only workaround I've come up with is to use a second type parameter and cast between the two:
def myMethod[T <% Comparable[T], U <: Comparable[U]](arg: T): T =
otherMethod(arg.asInstanceOf[U]).asInstanceOf[T]
This works, but it's ugly. Is there a better way?
Would either of the following work?
Make the T's view bound consistent in both methods,
def otherMethod[T <% Comparable[T]](arg: T): T = arg
def myMethod[T <% Comparable[T]](arg: T): T = otherMethod(arg)
Introduce a new type parameter U <: Comparable[U] and an implicit conversion from T to U,
def otherMethod[T <: Comparable[T]](arg: T): T = arg
def myMethod[U <: Comparable[U], T <% U](arg: T): U = otherMethod(arg)
The problem with your version is that T <% Comparable[T] converts T to type Comparable[T], but this does not satisfy the recursive type T <: Comparable[T <: Comparable[T <: ...]] (pseudocode) that otherMethod expects.
Update. To use either otherMethod or myMethod with Scala's Int, you will need to help the type inferencer a little bit,
myMethod(2) // Int value types don't implement Comparable
myMethod(2: java.lang.Integer) // Apply implicit conversion (Int => java.lang.Integer)
Update 2. In the comments, you said you're willing to make myMethod a little uglier to improve type inference at the call site. Here's a way,
def myMethod[U <: Comparable[U], T](arg: T)
(implicit ev1: T => U, ev2: T => Comparable[U]): U = otherMethod(arg)
myMethod(2) // returns java.lang.Integer(2)
The trick is to use two implicit conversions: ev1 actually gets applied, and ev2 is there only to aid type inference. The latter requires Scala to search its implicits for a conversion of type Int => Comparable[U]. In this case, only one such conversion can be found, which fixes U = java.lang.Integer.
By the way, try compiling this code with scalac -Xprint:typer. You'll see that the same implicit, Predef.int2Integer, is used for both ev1 and ev2 parameters.
Side note: it's best to avoid asInstanceOf casts because those defeat the soundness of the Scala type system.