I'm struggling with my DSL building attempt
My method should take multiple pairs of (A, B), ideally I'm looking for this kind of code:
myMethod(
a1 -> b1,
a2 -> b2,
a3 -> b3)
my A and B classes happened to often be constructed with a single string parameter, so i added implicit converters looking like this:
implict def stringToA(s: String): A = new A(s)
implict def stringToB(s: String): B = new B(s)
but arrow operator(for pair construction) doesn't pick up implicit converters:
object Test {
case class A(s: String)
case class B(s: String)
def myMethod(pairs: (A, B)*): Unit = Unit
implicit def stringToA(s: String): A = A(s)
implicit def stringToB(s: String): B = B(s)
myMethod(new Tuple2("a", "b")) // works just fine
myMethod("a" -> "b") // Error: Type mismatch, expected: (Test.A, Test.B), actual: (String, String)
}
any ideas?
The -> method is made available on all Scala types by an implicit conversion (any2ArrowAssoc in scala.Predef). For reasons of sanity, Scala doesn't allow multiple implicit conversions to "chain" on the same expression. You could:
Define an explicit -> method on your A class. Unfortunately since the default implicit conversion would conflict with this you might then have to build your code with -Yno-predef to avoid the conflict
Define a method on your A class with a visually similar name that doesn't conflict, e.g. ~> or ยป
Call myMethod(("a", "b")) if you like that syntax (this syntax is supported directly, not via an implicit conversion)
Make myMethod accept (String, String)* and do the conversion to A and B inside.
Edit: You could also define an implicit conversion from (String, String) to (A, B) as #BenReich suggests.
You can define an implicit conversion from (String, String) to (A, B):
implicit def toABTuple(in: (String, String)) = (A(in._1), B(in._2))
It's failing because its converting to a tuple before applying your implicits, so it's not a plain String -> A/B anymore.
Related
Since Scala 2.12 (or is it 2.13, can't be sure), the compiler can infer latent type arguments across multiple methods:
def commutative[
A,
B
]: ((A, B) => (B, A)) = {???} // implementing omitted
val a = (1 -> "a")
val b = commutative.apply(a)
The last line successfully inferred A = Int, B = String, unfortunately, this requires an instance a: (Int, String) to be given.
Now I'd like to twist this API for a bit and define the following function:
def findApplicable[T](fn: Any => Any)
Such that findApplicable[(Int, String)](commutative) automatically generate the correct function specialised for A = Int, B = String. Is there a way to do it within the language's capability? Or I'll have to upgrade to scala 3 to do this?
UPDATE 1 it should be noted that the output of commutative can be any type, not necessarily a Function2, e.g. I've tried the following definition:
trait SummonedFn[-I, +O] extends (I => O) {
final def summon[II <: I]: this.type = this
}
Then redefine commutative to use it:
def commutative[
A,
B
]: SummonedFn[(A, B), (B, A)] = {???} // implementing omitted
val b = commutative.summon[(Int, String)]
Oops, this doesn't work, type parameters don't get equal treatment like value parameters
If at some point some call-site knows the types of arguments (they aren't actually Any => Any) it is doable using type classes:
trait Commutative[In, Out] {
def swap(in: In): Out
}
object Commutative {
def swap[In, Out](in: In)(implicit c: Commutative[In, Out]): Out =
c.swap(in)
implicit def tuple2[A, B]: Commutative[(A, B), (B, A)] =
in => in.swap
}
At call site:
def use[In, Out](ins: List[In])(implicit c: Commutative[In, Out]): List[Out] =
ins.map(Commutative.swap(_))
However, this way you have to pass both In as well as Out as type parameters. If there are multiple possible Outs for a single In type, then there is not much you can do.
But if you want to have Input type => Output type implication, you can use dependent types:
trait Commutative[In] {
type Out
def swap(in: In): Out
}
object Commutative {
// help us transform dependent types back into generics
type Aux[In, Out0] = Commutative[In] { type Out = Out0 }
def swap[In](in: In)(implicit c: Commutative[In]): c.Out =
c.swap(in)
implicit def tuple2[A, B]: Commutative.Aux[(A, B), (B, A)] =
in => in.swap
}
Call site:
// This code is similar to the original code, but when the compiler
// will be looking for In it will automatically figure Out.
def use[In, Out](ins: List[In])(implicit c: Commutative.Aux[In, Out]): List[Out] =
ins.map(Commutative.swap(_))
// Alternatively, without Aux pattern:
def use2[In](ins: List[In])(implicit c: Commutative[In]): List[c.Out] =
ins.map(Commutative.swap(_))
def printMapped(list: List[(Int, String)]): Unit =
println(list)
// The call site that knows the input and provides implicit
// will also know the exact Out type.
printMapped(use(List("a" -> 1, "b" -> 2)))
printMapped(use2(List("a" -> 1, "b" -> 2)))
That's how you can solve the issue when you know the exact input type. If you don't know it... then you cannot use compiler (neither in Scala 2 nor in Scala 3) to generate this behavior as you have to implement this functionality using some runtime reflection, e.g. checking types using isInstanceOf, casting to some assumed types and then running predefined behavior etc.
I'm not sure I understand the question 100%, but it seems like you want to do some kind of advanced partial type application. Usually you can achieve such an API by introducing an intermediary class. And to preserve as much type information as possible you can use a method with a dependent return type.
class FindApplicablePartial[A] {
def apply[B](fn: A => B): fn.type = fn
}
def findApplicable[A] = new FindApplicablePartial[A]
scala> def result = findApplicable[(Int, String)](commutative)
def result: SummonedFn[(Int, String),(String, Int)]
And actually in this case since findApplicable itself doesn't care about type B (i.e. B doesn't have a context bound or other uses), you don't even need the intermediary class, but can use a wildcard/existential type instead:
def findApplicable[A](fn: A => _): fn.type = fn
This works just as well.
The title is attempting to describe the following subtyping
implicitly[Map[Int, String] <:< Iterable[(Int, String)]]
Type parameter A is inferred to (Int, String) here
def foo[A](cc: Iterable[A]): A = cc.head
lazy val e: (Int, String) = foo(Map.empty[Int, String])
however attempting to achieve similar effect using type parameter bounds the best I can do is explicitly specifying arity of the type constructor like so
def foo[F[x,y] <: Iterable[(x,y)], A, B](cc: F[A, B]): (A, B) = cc.head
lazy val e: (Int, String) = foo(Map.empty[Int, String])
because the following errors
def foo[F[x] <: Iterable[x], A](cc: F[A]) = cc.head
lazy val e: (Int, String) = foo(Map.empty[Int, String])
// type mismatch;
// [error] found : A
// [error] required: (Int, String)
// [error] lazy val e: (Int, String) = foo(Map.empty[Int, String])
// [error] ^
Hence using Iterable as upper bound it seems we need one signature to handle unary type constructors Seq and Set, and a separate signature to handle 2-arity type constructor Map
def foo[F[x] <: Iterable[x], A](cc: F[A]): A // When F is Seq or Set
def foo[F[x,y] <: Iterable[(x,y)], A, B](cc: F[A, B]): (A, B) // When F is Map
Is there a way to have a single signature using type bounds that works for all three? Putting it differently, how could we write, say, an extension method that works across all collections?
I think the issue here is that F is set to Map, and kindness is wrong. You would have to have say: I have some type X, that extends F[A], so that when I upcast it, I can use it as F[A] - which in turn we want to be a subtype of Iterable[A]. If we ask about it this way, it sounds hard.
Which is why I personally would just stay at:
# def foo[A](x: Iterable[A]): A = x.head
defined function foo
# foo(List(1 -> "test"))
res24: (Int, String) = (1, "test")
# foo(Map(1 -> "test"))
res25: (Int, String) = (1, "test")
"Give me any x that is an instance of Iterable[A] for A".
If I had to do some derivation... I would probably also go this way. I think this limitation is the reason CanBuildFrom works the way it works - providing matching for part of the type is hard, especially in cases like Map, so let's provide a whole type at once as a parameter, to limit the number of inference needed.
I have the following use case which occurs often in my code:
A Collection[A]
An implicit conversion A to B
and I want to obtain a collection of B. I can use implicitly like the following:
case class Items(underlying:List[B])
import B._
def apply(a:List[A]):Items = {
val listOfB= a.map {implicitly[A=>B]}
Items(listOfB)
}
What is the most elegant way to do that in Scala, maybe with the help of Scalaz of doing the same?
Edit: the goal of my question is to find an idiomatic way, a common approach among libraries/developers. In such a sense developing my own pimp-my-library solution is something I dislike, because other people writing my code would not know the existence of this conversion and would not use it, and they will rewrite their own. I favour using a library approach for this common functions and that's why I am wondering whether in Scalaz it exists such a feature.
It's pretty straightforward if you know the types. First implicit conversion from A to B:
implicit def conversion(a: A): B = //...
then you need implicit conversion from List[S] to List[T] where S and T are arbitrary types for which implicit conversion from S to T exists:
implicit def convList[S, T](input: List[S])(implicit c: S => T): List[T] =
input map c
This should then work:
val listOfA: List[A] = //...
val listOfB: List[B] = listOfA
which is resolved by the compiler to:
val listOfB: List[B] = convList(listOfA)(conversion)
where S is A and T is B.
I wouldn't use an implicit conversion here, but a view bound in the class:
case class Foo(x: Int)
case class Bar(y: Int)
implicit def foo2Bar(foo: Foo) = Bar(foo.x)
case class Items[A <% Bar](xs: List[A]) {
def apply(x: Int): Bar = xs(x)
}
You can now create an instance of Items with a list of Foo and internally use them, as if they were Bars.
scala> Items(List(Foo(1)))
res8: Items[Foo] = Items(List(Foo(1)))
scala> res8(0)
res9: Bar = Bar(1)
edit:
Some clarification, on why I would not use an implicit conversion:
Implicit conversions can be dangerous, when they are in scope and accidentally convert things, that they shouldn't convert. I would always convert stuff explicitly or via view bounds, because then I can control it, also implicit conversion may shrink the size of your code, but also makes it harder to understand for others. I would only use implicit conversion for the 'extend my library' pattern.
edit2:
You could however add a method to the collection types, that does this conversion, if such a method is in scope:
trait Convertable[M[A], A] {
def convertTo[B](implicit f: A => B): M[B]
}
implicit def list2Convertable[A](xs: List[A]) = new Convertable[List, A] {
def convertTo[B](implicit f: A => B) = xs.map(f)
}
scala> implicit def int2String(x: Int) = x.toString
int2String: (x: Int)String
scala> List(1,2,3).convertTo[String]
res0: List[String] = List(1, 2, 3)
Instead of using another implicit conversion here, I would probably use a typeclass instead, but I think you get the basic idea.
Works starting with Scala 2.10:
implicit class ListOf[A](val list: List[A]) {
def of[B](implicit f: A => B): List[B] = list map f
}
implicit def int2String(i: Int) = i.toString
// Usage
List(1,2,3).of[String]
In my code, I'm using a more general version adapted from Tomasz' solution above which handles all Traversable instances
/** Implicit conversion for Traversable instances where the elements are convertable */
implicit def convTrav[S, T, I[S] <: Traversable[S]](input: I[S])(implicit c: S => T): I[T] =
(input map c).asInstanceOf[I[T]]
(This is working for me, although I'm keen to know if any more experienced Scala programmers think this is a bad idea for any reason, apart from the usual caveats about implicit conversions)
I'm running into some kind of quirk in the Scala type system that has me a bit stumped. I am trying to make a class that extends Map[String,String] and I can't quite figure out how to implement the + method in such a way that the compiler accepts it.
Here's the code I have now:
class ParamMap(val pairs:List[(String,String)] = Nil) extends Map[String,String] {
lazy val keyLookup = Map() ++ pairs
override def get(key: String): Option[String] = keyLookup.get(key)
override def iterator: Iterator[(String, String)] = pairs.reverseIterator
/**
* Add a key/value pair to the map
*/
override def + [B1 >: String](kv: (String, B1)) = new ParamMap(kv :: pairs)
/**
* Remove all values for the given key from the map
*/
override def -(key: String): ParamMap = new ParamMap(pairs.filterNot(_._1 == key))
/**
* Remove a specific pair from the map
*/
def -(kv: (String, String)) : ParamMap = new ParamMap(pairs - kv)
}
Scala tells me this:
type mismatch; found: (String, B1) required: (String, String)
I believe this is because B1 is allowed to be a subtype of String but my constructor expects just a String (?). My original attempt was:
override def +(kv: (String, String)) = new ParamMap(kv :: pairs)
But this complained because the type signature didn't match the trait:
class ParamMap needs to be abstract, since method + in trait Map of type [B1 >: String](kv: (String, B1))scala.collection.immutable.Map[String,B1] is not defined
method + overrides nothing
I'm new to Scala and I think I'm getting over my head here in terms of how the type system works. Perhaps I'll try messing with casting but I have a feeling there might be a "better way" that, if I know it, will save me a lot of trouble in the future.
Any ideas?
Some background about Scala's type system.
The syntax B1 >: String means that B1 is a supertype of String. So B1 is less specific, and can't be cast to a String. Conversely, B1 <: String would be a subtype relationship.
The definition of the Map trait is Map [A, +B], where A represents the type of the key and B the type of the value. The +B notation says that Map is covariant in the key type, which means that T <: S implies Map[A, T] <: Map[A, S].
The full type of the Map.+ method is + [B1 >: B] (kv: (A, B1)): Map[A, B1]. The covariance of B kind of forces the use of B1 >: B. Here's an example of how it works: given a map m: Map[String, String] adding a key-value pair with a less specific type kv : (String, Any) will result in a less specific map, (m + kv): Map[String, Any].
The last point illustrates the problem with your ParamMap definition. According to the Map interface, one should be able to add a key of type Any to a map of type ParamMap <: Map[String, String] and get back a Map[String, Any]. But you're trying to define ParamMap.+ to always return ParamMap[String, String], which is incompatible with Map.+.
One way to fix the problem is to give ParamMap an explicit type parameter, something like (warning untested),
class ParamMap[B](val pairs:List[(String,String)] = Nil) extends Map[String, B] {
...
override def + [B1 >: B](kv: (String, B1)) = new ParamMap[B1](kv :: pairs)
}
but this may not be what you want. I don't think there's a way to fix the value type as String and implement the Map[String, String] interface.
Given all the above, why does the code in your answer compile? You've actually uncovered a limitation (unsoundness) of Scala's pattern matching, and it can lead to run-time crashes. Here's a simplified example:
def foo [B1 >: String](x: B1): Int = {
val (s1: Int, s2: Int) = (x, x)
s1
}
Although this compiles, it doesn't do anything useful. In fact, it will always crash with a MatchError:
scala> foo("hello")
scala.MatchError: (hello,hello) (of class scala.Tuple2)
at .foo(<console>:9)
at .<init>(<console>:10)
at .<clinit>(<console>)
...
In your answer, you've basically told the compiler to convert a B1 instance to a String, and if the conversion doesn't work, you'll get a runtime crash. It's equivalent to an unsafe cast,
(value: B1).asInstanceOf[String]
You're correct that your constructor expects a value of type List[String, String], but the issue isn't that B1 could be a subclass of String, it's that it could be a superclass -- this is what the B1 :> String notation indicates.
At first glance, you might wonder why the parent Map class would have the method typed this way. In fact, the return type of the + method you're attempting to override there is Map[String, B1]. In the context of a general map, though, this makes sense. Suppose you had the following code:
class Parent
class Child extends Parent
val childMap = Map[String, Child]("Key" -> new Child)
val result = childMap + ("ParentKey" -> new Parent)
The type of result would then have to be Map[String, Parent]. In light of this, the type restrictions on the + method in Map makes sense, but your fixed-type map isn't capable of fulfilling what the method is designed to be able to do. Its signature allows you to pass in a value of e.g. type (String, AnyRef), but using the method definition you gave in your followup answer, you'll get a MatchError when it tries to perform the assignment to key and value.
Does that make sense?
I ran the same problem with a colleague, when trying to build a Bag[T] which is a Map[T,Int]. We found two different solutions:
Implement Traversable rather than Map with appropriate Builder and CanBuildFrom, and add the useful map methods (get,+,-). If you need to pass the collection to a function taking maps as arguments, you can use implicit conversions. Here is our full Bag implementation: https://gist.github.com/1136259
Stay simple:
object collection {
type ParamMap = Map[String,String]
object ParamMap {
def apply( pairs: List[(String,String)] = Nil ) = Map( pairs:_* )
}
}
The compiler does seem to accept this one:
override def + [B1 >: String](kv: (String, B1)) = {
val (key:String, value:String) = kv
new ParamMap((key,value) :: pairs)
}
But I don't know why that is better than the original. I suppose this is an acceptable solution if nobody has a better one.
Let's assume I want to create a trait that I can mix in into any Traversable[T]. In the end, I want to be able to say things like:
val m = Map("name" -> "foo") with MoreFilterOperations
and have methods on MoreFilterOperations that are expressed in anything Traversable has to offer, such as:
def filterFirstTwo(f: (T) => Boolean) = filter(f) take 2
However, the problem is clearly that T is not defined as a type parameter on MoreFilterOperations. Once I do that, it's doable of course, but then my code would read:
val m = Map("name" -> "foo") with MoreFilterOperations[(String,String)]
or if I define a variable of this type:
var m2: Map[String,String] with MoreFilterOperations[(String,String)] = ...
which is way to verbose for my taste. I would like to have the trait defined in such a way that I could write the latter as:
var m2: Map[String,String] with MoreFilterOperations
I tried self types, abstract type members, but it hasn't resulted in anything useful. Any clues?
Map("name" -> "foo") is a function invocation and not a constructor, this means that you can't write:
Map("name" -> "foo") with MoreFilterOperations
any more that you can write
val m = Map("name" -> "foo")
val m2 = m with MoreFilterOperations
To get a mixin, you have to use a concrete type, a naive first attempt would be something like this:
def EnhMap[K,V](entries: (K,V)*) =
new collection.immutable.HashMap[K,V] with MoreFilterOptions[(K,V)] ++ entries
Using a factory method here to avoid having to duplicate the type params. However, this won't work, because the ++ method is just going to return a plain old HashMap, without the mixin!
The solution (as Sam suggested) is to use an implicit conversion to add the pimped method. This will allow you to transform the Map with all the usual techniques and still be able to use your extra methods on the resulting map. I'd normally do this with a class instead of a trait, as having constructor params available leads to a cleaner syntax:
class MoreFilterOperations[T](t: Traversable[T]) {
def filterFirstTwo(f: (T) => Boolean) = t filter f take 2
}
object MoreFilterOperations {
implicit def traversableToFilterOps[T](t:Traversable[T]) =
new MoreFilterOperations(t)
}
This allows you to then write
val m = Map("name"->"foo", "name2"->"foo2", "name3"->"foo3")
val m2 = m filterFirstTwo (_._1.startsWith("n"))
But it still doesn't play nicely with the collections framework. You started with a Map and ended up with a Traversable. That isn't how things are supposed to work. The trick here is to also abstract over the collection type using higher-kinded types
import collection.TraversableLike
class MoreFilterOperations[Repr <% TraversableLike[T,Repr], T] (xs: Repr) {
def filterFirstTwo(f: (T) => Boolean) = xs filter f take 2
}
Simple enough. You have to supply Repr, the type representing the collection, and T, the type of elements. I use TraversableLike instead of Traversable as it embeds its representation; without this, filterFirstTwo would return a Traversable regardless of the starting type.
Now the implicit conversions. This is where things get a bit trickier in the type notation. First, I'm using a higher-kinded type to capture the representation of the collection: CC[X] <: Traversable[X], this parameterises the CC type, which must be a subclass of Traversable (note the use of X as a placeholder here, CC[_] <: Traversable[_] does not mean the same thing).
There's also an implicit CC[T] <:< TraversableLike[T,CC[T]], which the compiler uses to statically guarantee that our collection CC[T] is genuinely a subclass of TraversableLike and so a valid argument for the MoreFilterOperations constructor:
object MoreFilterOperations {
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)
}
So far, so good. But there's still one problem... It won't work with maps, because they take two type parameters. The solution is to add another implicit to the MoreFilterOperations object, using the same principles as before:
implicit def mapToFilterOps[CC[KX,VX] <: Map[KX,VX], K, V]
(xs: CC[K,V])(implicit witness: CC[K,V] <:< TraversableLike[(K,V),CC[K,V]]) =
new MoreFilterOperations[CC[K,V],(K,V)](xs)
The real beauty comes in when you also want to work with types that aren't actually collections, but can be viewed as though they were. Remember the Repr <% TraversableLike in the MoreFilterOperations constructor? That's a view bound, and permits types that can be implicitly converted to TraversableLike as well as direct subclasses. Strings are a classic example of this:
implicit def stringToFilterOps
(xs: String)(implicit witness: String <%< TraversableLike[Char,String])
: MoreFilterOperations[String, Char] =
new MoreFilterOperations[String, Char](xs)
If you now run it on the REPL:
val m = Map("name"->"foo", "name2"->"foo2", "name3"->"foo3")
// m: scala.collection.immutable.Map[java.lang.String,java.lang.String] =
// Map((name,foo), (name2,foo2), (name3,foo3))
val m2 = m filterFirstTwo (_._1.startsWith("n"))
// m2: scala.collection.immutable.Map[java.lang.String,java.lang.String] =
// Map((name,foo), (name2,foo2))
"qaxfwcyebovjnbointofm" filterFirstTwo (_ < 'g')
//res5: String = af
Map goes in, Map comes out. String goes in, String comes out. etc...
I haven't tried it with a Stream yet, or a Set, or a Vector, but you can be confident that if you did, it would return the same type of collection that you started with.
It's not quite what you asked for, but you can solve this problem with implicits:
trait MoreFilterOperations[T] {
def filterFirstTwo(f: (T) => Boolean) = traversable.filter(f) take 2
def traversable:Traversable[T]
}
object FilterImplicits {
implicit def traversableToFilterOps[T](t:Traversable[T]) = new MoreFilterOperations[T] { val traversable = t }
}
object test {
import FilterImplicits._
val m = Map("name" -> "foo", "name2" -> "foo2", "name3" -> "foo3")
val r = m.filterFirstTwo(_._1.startsWith("n"))
}
scala> test.r
res2: Traversable[(java.lang.String, java.lang.String)] = Map((name,foo), (name2,foo2))
Scala standard library uses implicits for this purpose. E.g. "123".toInt. I think its the best way in this case.
Otherwise you'll have to go through full implementation of your "map with additional operations" since immutable collections require creation of new instances of your new mixed class.
With mutable collections you could do something like this:
object FooBar {
trait MoreFilterOperations[T] {
this: Traversable[T] =>
def filterFirstTwo(f: (T) => Boolean) = filter(f) take 2
}
object moreFilterOperations {
def ~:[K, V](m: Map[K, V]) = new collection.mutable.HashMap[K, V] with MoreFilterOperations[(K, V)] {
this ++= m
}
}
def main(args: Array[String]) {
val m = Map("a" -> 1, "b" -> 2, "c" -> 3) ~: moreFilterOperations
println(m.filterFirstTwo(_ => true))
}
}
I'd rather use implicits.