Let's assume we have a map indexed by enum values like this
sealed trait A
case object B extends A
case object C extends A
type SafeMap = Map[A, String]
Would it be possible to somehow enforce the exhaustiveness of such map?
Maybe with dependent types? If not in Scala than Idris is interesting as well.
To clarify, I would like to have a generic constraint for any ADT that check that Map has an entry defined for each variant of ADT.
well a Map[A,String] for an ADT A is pretty much a function A => String
You can do
def foo(a:A):String = a match {
case ...
}
with the fatal-warning flag for the compiler (else non-exhaustive patternmatches are only warnings) this should get you pretty far.
PS: you should probably make your case classes/object final
Related
I'm using Slick on a project and for that I require a Slick representation of my rows and then also an in memory representation. I'm going to use a much simpler example here for brevity. Say for example I have both these types:
type RawType =
(String, Int, Boolean)
type RawTypeRep =
(Rep[String], Rep[Int], Rep[Boolean])
Is there a way to generate one from the other so I don't have to update them in lockstep?
Or perhaps generate them both from a case class? I do have a case class representation, but it's actually slightly different from the types I have because when I hydrate the case class I do some mutations which results in type changes.
This seems like a job for shapeless, though I'm not sure exactly the best way to apply it to your case. The best I can think of is not to generate one from the other, but to at least verify at compile time that the two match up:
import shapeless._
import shapeless.poly._
import shapeless.ops.tuple._
object ToRep extends (Id ~> Rep) {
def apply[A](a: A): Rep[A] = ???
}
type CheckRep[A, B] = Mapper.Aux[A, ToRep.type, B]
type RawType = (String, Int, Boolean)
type RawTypeRep = (Rep[String], Rep[Int], Rep[Boolean])
type RawTypeRepWrong = (Rep[String], Rep[String], Rep[Boolean])
implicitly[CheckRep[RawType, RawTypeRep]] // compiles
implicitly[CheckRep[RawType, RawTypeRepWrong]] // compile error
I am guessing there's probably a better way to handle this given a step back to a larger context. Might be worthwhile to skim the documentation for shapeless to see the kinds of things it can do and see if that gives you any idea.
I have the following code
def addKitten(kitten: Kitten): EitherT[Future, GenericError, UUID] = {
val futureOfEither = db.run { // returns a Future[Int] with the number of rows written
kittens += kitten
}.map {
case 1 => kitten.uuid.asRight
case _ => GenericError.SpecificError.asLeft
}
EitherT(futureOfEither)
}
Where SpecificError is a subclass of GenericError. For some reason it does not compile complaining that a SpecificError is not a GenericError. Is it right?
I mean, Either[A, B] should be immutable, so why not making it covariant? Am I missing something?
The same issue for XorT and OptionT was raised here. The reply was:
In Scala, variance has both positives and negatives (perhaps that's how they decided on variance notation! :P). These pros/cons have been discussed numerous times in various venues, so I won't go into them now, but in my opinion at the end of the day you kind of have to settle on "to each their own".
I think this "to each their own" perspective implies that you can't force variance on a type constructor. A concrete example is scalaz.Free in the 7.0 series. It forces the S type constructor to be covariant. At the time I often wanted to wrap a Coyoneda in Free. The most recent versions of Cats and Scalaz have the Coyoneda essentially built into the Free, so this particular use might not be as desired now, but the general principle applies. The problem is that Coyoneda is invariant, so you simply couldn't do this (without a mess of #uncheckedVariance)! By making type constructor parameters invariant, you may end up forcing people to be more explicit about types, but I think that it beats the alternative, where you can prevent them from being able to use your type at all.
Ok, I found a workaround.
I still have no idea why the EitherT is not covariant but you have to remember that the Either itself is covariant.
So the trick is to tell the compiler to use an upper bound for the EitherT creation:
EitherT[Future, GenericError, UUID](futureOfEither)
Having more than one error as left works too (because the compiler is forced to find the LUB) but GenericError has to extend Product and Serializable if it’s a trait and SpecificError is a case class (refer to this question as to why)
...
}.map {
case 1 => kitten.uuid.asRight
case 2 => GenericError.SpecificError2.asLeft
case _ => GenericError.SpecificError.asLeft
}
EitherT(futureOfEither)
As another quite neat workaround you could specify abstract types in flatMap[E, T] type parameters, for example:
// Given ADT
trait Err
case class E1() extends Err
case class E2() extends Err
// We could do (pseudo-code)
EitherT(E1()).flatMap[Err, Int] { x => 100 }
It is in case of FlatMap. For Map you could only transform value and type on the right side.
I am porting over code that was written in Python over to Scala.
This python code [0,{}] creates a list of an integer and a dictionary.
The dictionary in this case will have keys that are strings, but the values can either be an int or a string.
I want to do something similar in Scala. I believe I would have to do something like:
List[Any] = (<int>, scala.collection.mutable.Map[String, Any](...))
I plan to bencode this list so the underlying type of 'Any' matters. How do I change the types so that the list can be bencoded?
For the most part, using Any is not the right thing to do in Scala.
Since your problem is that the map values can be either Int or String, rather than trying to use their common type (Any), make your own new type hierarchy to represent the possibilities:
sealed trait BData
case class BInt(i: Int) extends BData
case class BString(s: String) extends BData
Now your map will have a type signature of Map[String, BData]
Using BData as the "underlying type" is better than using Any because there is no ambiguity to it. It has exactly two subclasses; compared with Any, where your values might be a List[(Foo, Bar)], for all the compiler knows.
I'll take dylan answer little further.
Scala have strong type system and you should use it.
When you are using Any which is like Object in java, you lose the type.
You should define the types and let the compiler work for you:
sealed trait BData[T] {
val value: T
}
implicit class BInt(val value: Int) extends BData[Int]
implicit class BString(val value: String) extends BData[String]
the 'implicit' declaration makes the compiler "Convert" the types for you, so you can use it like this:
val bDataMap = scala.collection.mutable.Map[String, BData[_]]()
val l = List((1, bDataMap))
l(0)._2 += ("myKey1" -> "Str")
l(0)._2 += ("myKey2" -> 2)
l(0)._2("myKey1").value
and so on....
Part of a current project involves converting from types coupled to a database and a generic type used when serializing the results out to clients via Json, the current implementation in Scala uses type inference to correctly perform the transformation, using Scala's TypeTag:
def Transform[A: TypeTag](objects:Seq[A]):Seq[Children] = typeOf[A] match {
case pc if pc =:= typeOf[ProductCategory] =>
TransformProductCategory(objects.asInstanceOf[Seq[ProductCategory]])
case pa if pa =:= typeOf[ProductArea] =>
TransformProductArea(objects.asInstanceOf[Seq[ProductArea]])
case pg if pg =:= typeOf[ProductGroup] =>
TransformProductGroup(objects.asInstanceOf[Seq[ProductGroup]])
case psg if psg =:= typeOf[ProductSubGroup] =>
TransformProductSubGroup(objects.asInstanceOf[Seq[ProductSubGroup]])
case _ =>
throw new IllegalArgumentException("Invalid transformation")
}
The types used as input are all case classes and are defined internally within the application, for example:
case class ProductCategory(id: Long, name: String,
thumbnail: Option[String],
image:Option[String],
sequence:Int)
This approach, although suitable at the moment, doesn't feel functional or scalable when potentially more DB types are added. I also feel using asInstanceOf should be redundant as the type has already been asserted. My limited knowledge of implicits suggests they could be used instead to perform the transformation, and remove the need for the above Transform[A: TypeTag](objects:Seq[A]):Seq[Children] method altogether. Or maybe there is a different approach I should have used instead?
You can define a trait like this:
trait Transformer[A] {
def transformImpl(x: Seq[A]): Seq[Children]
}
Then you can define some instances:
object Transformer {
implicit val forProduct = new Transformer[ProductCategory] {
def transformImpl(x: Seq[ProductCategory]) = ...
}
...
}
And then finally:
def transform[A: Transformer](objects:Seq[A]): Seq[Children] =
implicitly[Transformer[A]].transformImpl(objects)
Preferably, you should define your implicit instances either in Transformer object or in objects corresponding to your category classes.
I'm not sure how your program is supposed to work exactly, nor do I know if any of your Transforms are self-made types or something you pulled from a library, however I may have a solution anyway
Something match is really good with, is case classes
So rather than manually checking the type of the input data, you could maybe wrap them all in case classes (if you need to bring data with you) or case objects (if you don't)
That way you can do something like this:
// this code assumes ProductCategory, ProductArea, etc.
// all extends the trait ProductType
def Transform(pType: ProductType): Seq[Children] = pType match {
case ProductCategory(objects) => TransformProductCategory(objects)
case ProductArea(objects) => TransformProductArea(objects)
case ProductGroup(objects) => TransformProductGroup(objects)
case ProductSubGroup(objects) => TransformProductSubGroup(objects)
}
By wrapping everything in a case class, you can specify exactly which type of data you want to bring along, and so long as they (the case classes, not the data) all inherit from the same class/trait, you should be fine
And since there's only a little handful of classes that extend ProductType you don't need the default case, because there is no default case!
Another benefit of this, is that it's infinitely expandable; just add more cases and case classes!
Note that this solution requires you to refactor your code a LOT, so bare that in mind before you throw yourself into it.
I want to map from class tokens to instances along the lines of the following code:
trait Instances {
def put[T](key: Class[T], value: T)
def get[T](key: Class[T]): T
}
Can this be done without having to resolve to casts in the get method?
Update:
How could this be done for the more general case with some Foo[T] instead of Class[T]?
You can try retrieving the object from your map as an Any, then using your Class[T] to “cast reflectively”:
trait Instances {
private val map = collection.mutable.Map[Class[_], Any]()
def put[T](key: Class[T], value: T) { map += (key -> value) }
def get[T](key: Class[T]): T = key.cast(map(key))
}
With help of a friend of mine, we defined the map with keys as Manifest instead of Class which gives a better api when calling.
I didnt get your updated question about "general case with some Foo[T] instead of Class[T]". But this should work for the cases you specified.
object Instances {
private val map = collection.mutable.Map[Manifest[_], Any]()
def put[T: Manifest](value: T) = map += manifest[T] -> value
def get[T: Manifest]: T = map(manifest[T]).asInstanceOf[T]
def main (args: Array[String] ) {
put(1)
put("2")
println(get[Int])
println(get[String])
}
}
If you want to do this without any casting (even within get) then you will need to write a heterogeneous map. For reasons that should be obvious, this is tricky. :-) The easiest way would probably be to use a HList-like structure and build a find function. However, that's not trivial since you need to define some way of checking type equality for two arbitrary types.
I attempted to get a little tricky with tuples and existential types. However, Scala doesn't provide a unification mechanism (pattern matching doesn't work). Also, subtyping ties the whole thing in knots and basically eliminates any sort of safety it might have provided:
val xs: List[(Class[A], A) forSome { type A }] = List(
classOf[String] -> "foo", classOf[Int] -> 42)
val search = classOf[String]
val finalResult = xs collect { case (`search`, result) => result } headOption
In this example, finalResult will be of type Any. This is actually rightly so, since subtyping means that we don't really know anything about A. It's not why the compiler is choosing that type, but it is a correct choice. Take for example:
val xs: List[(Class[A], A) forSome { type A }] = List(classOf[Boolean] -> 'bippy)
This is totally legal! Subtyping means that A in this case will be chosen as Any. It's hardly what we want, but it is what you will get. Thus, in order to express this constraint without tracking all of the types individual (using a HMap), Scala would need to be able to express the constraint that a type is a specific type and nothing else. Unfortunately, Scala does not have this ability, and so we're basically stuck on the generic constraint front.
Update Actually, it's not legal. Just tried it and the compiler kicked it out. I think that only worked because Class is invariant in its type parameter. So, if Foo is a definite type that is invariant, you should be safe from this case. It still doesn't solve the unification problem, but at least it's sound. Unfortunately, type constructors are assumed to be in a magical super-position between co-, contra- and invariance, so if it's truly an arbitrary type Foo of kind * => *, then you're still sunk on the existential front.
In summary: it should be possible, but only if you fully encode Instances as a HMap. Personally, I would just cast inside get. Much simpler!