Inspired by my C# implementation of generic Strategy pattern I want to do the same in Scala. I want also make some functional programming to encapsulate Strategy algorithms inside a inherited class.
So what I've done yet is:
trait Strategy {
type T <: Strategy
type O
def Call(victim: T): O = {
strategy(victim)
}
var strategy: (this.T => this.O)
}
This is a trait which is a base of scaldfolding. I have also a StrategyFactory class:
case class StrategyFactory[T <: Strategy, O](str: T) {
def Call(x: (T => O)) = x(str)
}
object StrategyFactory {
}
And finally in my code I can create concrete Strategy:
class DownloadStrategy(path: String) extends Strategy {
type T = DownloadStrategy
type O = String
strategy = (dw: DownloadStrategy) => path + "aaaa"
}
object DownloadStrategy {
def apply(s: String) = new DownloadStrategy(s)
}
In my application code I have this:
var ds = DownloadStrategy("j")
val m = StrategyFactory[DownloadStrategy, String](ds)
var output = m.Call(ds.strategy)
Here works everything good.
I want to have functional strategies thus there is m.Call(ds.strategy)
But It is very dummy design because I cannot create a set of classes which will be extending DownloadStrategy. For example:
class ImageDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = ImageDownloadStrategy
type O = String
strategy = (ids: T) => path + ":ImageDownloadStrategy"
}
class VideoDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = VideoDownloadStrategy
type O = String
strategy = (ids: T) => path + ":VideoDownloadStrategy"
}
And so on. Basically I want to have one base class of some default strategy and subclasses are more specific implementations.
This brings me to application code where I would like to code something like this:
var ds: DownloadStrategy = null
request.getQueryString("t") match {
case "1" => ds = ImageDownloadStrategy("","","")
case "2" => ds = VideoDownloadStrategy("","","")
case "3" => ds = RawFileDownloadStrategy("","","")
case _ => ds = DownloadStrategy("")
}
var output = (StrategyFactory[DownloadStrategy, String](ds)).Call(ds.strategy)
I thought that when I write StrategyFactory[DownloadStrategy, String](ds) the compiler will be so smart enought that can figure if ImageDownloadStrategy is subclass of DownloadStrategy will could allow me to do some polimorphic calls but i cannot do it.
Another fact is that I need to overrides type T and type O in delivered class from DownloadStrategy but I dont have any idea how to do it.
Please give me some advices how to model this kind of behaviour.
EDIT(for pagoda_5b details)
As I have mentioned I have functional var strategy in trait Strategy which is var strategy: (this.T => this.O). This variable need to be overriden in classes implementing this trait. Also I have 2 generic types which T means subclass of concrete strategy and O indicate result type from def Call(...).
What I want to achive is having functional strategies inside subclass of Strategy and then make polimorphic calls. Here I have got DownloadStrategy which is default strategy and I have some subclasses with specicif algorithms. I want cast ImageDownloadStrategy to DownloadStrategy and use it as I showed in switch case statement.
Ok, I'll try to take a shot.
Since you can have function objects, you probably can simply do without any of the machinery of a Strategy hierarchy or factory whatsoever.
You can for example
//this is sort of a factory
object Strategies {
//a type alias to better define your selected functions
type Strategy[T, O] = T => O
//a set of methods to obtain the correct strategy "on demand"
def imageDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":ImageDownloadStrategy"
def videoDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":VideoDownloadStrategy"
def rawFileDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":RawDownloadStrategy"
//this is the fallback default
def download[T](path: String): Strategy[T, String] =
(t: T) =>
path + "aaaa"
}
object Client {
//make the strategies visible
import Strategies._
//processes the request
def process(request: Request): String = {
//here val means that the strategy variable won't be reassigned, ever
val strategy = selectStrategy[T](request.getQueryString("t")) //here we miss the type of the input value
//this assignment could be omitted if it's just returned
val output = strategy(??) //Here I'm missing the input to the strategy
output
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, String] =
selector match {
case "1" => imageDownload("","","")
case "2" => videoDownload("","","")
case "3" => rawFileDownload("","","")
case _ => download("")
}
}
As you can see, I'm missing what is the input value passed from the request to the strategy, so there are a couple holes in the process method
I don't know if this is what you need, but it could give you an idea why the strategy pattern is not so useful in functional languages, but rather needlessly cumbersome.
EDIT
Finally I found time to post real life example of downloading strategy in playframework.
object Download{
object Type extends Enumeration {
type Type = Value
val Image = "1"
val Video = "2"
val Pdf = "3"
val File = "4"
}
}
object Strategies {
type Strategy[T, O] = T => O
def imageDownload[T](): Strategy[T, java.io.File] =
(t: T) => {
//Receive download strategy information
val dw = t.asInstanceOf[DownloadStrategy]
//juicy code goes here
java.io.File.createTempFile("", "")
}
def videoDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
def rawFileDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
//this is the fallback default
def download[T](): Strategy[T, java.io.File] =
(t: T) => {
java.io.File.createTempFile("", "")
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, java.io.File] =
selector match {
case Download.Type.Image => {
imageDownload()
}
case Download.Type.Video => {
videoDownload()
}
case Download.Type.Pdf => {
rawFileDownload()
}
case Download.Type.File => {
rawFileDownload()
}
case _ => download()
}
}
case class DownloadStrategy(request: Request[AnyContent], path: String, file: Option[File]) {
}
//Controller code
def download(path: String) = Action {
implicit request =>
val file: Option[File] = FileStore.byPath(path, true)
val ds = DownloadStrategy(request, path, file)
//request.getQueryString("t") - Download type
val str = Strategies.selectStrategy[DownloadStrategy](request.getQueryString("t").getOrElse(""))
val x = str(ds)
Ok.sendFile(
content = x
)
}
Related
I have several methods which falsify a word, and I need to call them randomly, until one achieve to create a "human mistake".
I want to call a random method then another, until it's ok, but never twice the same.
Putting them in an array (or a list) imply that I rewrite an array each time I try a method, it's an ugly computational complexity and I'd like to write "Scala style" code, with minimum of var and mutability.
EDIT:
The Oleg Pyzhcov solution works well, but now I have some functions with String params, and other without. How to store functions and their call params in a collection?
val rand: Random = new Random()
def m1(a: String): Boolean = rand.nextBoolean()
def m2(): Boolean = rand.nextBoolean()
def m3(a: String, b: String): Boolean = rand.nextBoolean()
def m4(): Boolean = rand.nextBoolean()
def tryUntilOk(): Unit = {
def out = rand.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
.map(method => method()) // calling without params so error
.find(result => result) // stop when a method return true
}
EDIT 2:
DETAILS
I have several methods which tries to falsify a word, without guarantee that they achieve it. Some methods take the mood and tense of a verb, to change the tense or the mood, other just take the word correct writing to remove some letters, other take the gender and number of a noun to change it's gender.
I want to call a random method among all possible, and if it fail to falsify the word (for example the given noun only exist in the feminine form) then call another randomly. Repeating this operation until no more methods are available, so we give up.
The solution of Oleg is nice for the random part, but I can't find how to give to methods call parameters.
Concrete exemple:
package Counterfeiters
import Words.Noun
object Noun extends Counterfeiter[Noun] {
override def counterfeit(word: Noun): Unit = {
// For now I call methods without random order
// This one take only one String param
// And split letters that are duplicated like boot -> bot
word.currentWriting = splitDoubleLetters(word.correctWriting)
// Then we compare word.currentWriting and word.correctWriting
// If the word wasn't containing double letters, it isn't counterfeited
if (!word.isCounterfeited)
// This one take 5 String params
// And counterfeit the gender or the number of the word, randomly
word.currentWriting = counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number)
}
}
To apply the solution of Oleg, I just need to find how to store methods in a collection, with corresponding params. In this case (splitDoubleLetters, (word.correctWriting)) and (counterfeitGenderNumberWord, ("N", word.correctWriting, word.lemma, word.gender, word.number)).
SOLUTION
I did what Oleg advised in a comment:
object Noun extends Counterfeiter[Words.Noun] {
override def counterfeit(word: Words.Noun): Unit = {
if (word.isCounterfeited) return
def split: () => String = () => splitDoubleLetter(word.correctWriting)
def ctftGenderNumber: () => String = () => counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number)
val methods: immutable.Seq[() => String] = Stream(split, ctftGenderNumber)
val res: Option[String] = randomizer.shuffle(methods) // Shuffle methods
.map(method => method()) // Call them one by one
.find(result => result != word.correctWriting) // Until one counterfeit the word
word.currentWriting = res match {
case None => word.correctWriting // If every methods failed
case _ => res.get
}
}
}
SergGr explained well a possible architecture, I'll fit to this as it's clear.
You can find my complete project code on GitHub if you want to understand better what I do.
Using Stream for laziness and Random#shuffle method you can get:
import scala.util.Random
def m1(): Boolean = Random.nextBoolean()
def m2(): Boolean = Random.nextBoolean()
def m3(): Boolean = Random.nextBoolean()
def m4(): Boolean = Random.nextBoolean()
def out = Random.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
.map(method => method()) // pass in any necessary params
.find(result => !result) // do your check
Here, out has type Option[Boolean] since methods m1-m4 all return Boolean
I agree with Oleg that what you need is to convert all you methods into a collection of the same shape. I assume that in the word package you have base class Word with subclasses for different parts of the speech with different characteristics. Something like this:
abstract class Word(val correctWriting: String, var currentWriting: String, val lemma: String) {
def isCounterfeited: Boolean = !correctWriting.equals(currentWriting)
}
sealed trait Gender
case object Masculine extends Gender
case object Feminine extends Gender
case object Neutral extends Gender
sealed trait Number
case object Singular extends Number
case object Plural extends Number
class Noun(correctWriting: String, currentWriting: String, lemma: String, val gender: Gender, val number: Number) extends Word(correctWriting, currentWriting, lemma) {
}
and you have trait Counterfeiter defined as
trait Counterfeiter[-W <: Word] {
def counterfeit(word: W): Unit
}
Then you may define helper class RandomCompoundCounterfeiter
type SimpleCounterfeiter[W <: Word] = (String, W) => String
class RandomCompoundCounterfeiter[W <: Word](val children: Seq[SimpleCounterfeiter[W]]) extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
Random.shuffle(children).takeWhile(c => {
word.currentWriting = c(word.correctWriting, word)
!word.isCounterfeited
})
}
}
RandomCompoundCounterfeiter is the class the does the main job you were asking for: it applies other SimpleCounterfeiter in a random order. It does this by first shuffling the list of children (i.e. real counterfeiters) and applying them until after some word.isCounterfeited is finally true or the list is exhausted.
Note that RandomCompoundCounterfeiter re-shuffles counterfeiters on each call. If you want to have your order to be different between different runs of the application but the same for different words inside single run, just move the shuffling to the constructor.
Now define list of basic SimpleCounterfeiters functions such as
object Noun {
val singularCounterfeiter = (correctWriting: String, word: Noun) => {
if (word.number == Singular)
correctWriting
else
???
}
val pluralCounterfeiter = (correctWriting: String, word: Noun) => {
if (word.number == Plural)
correctWriting
else
???
}
def genderCounterfeiter(newGender: Gender): SimpleCounterfeiter[Noun] = (correctWriting: String, word: Noun) => {
if (word.gender == newGender)
correctWriting
else
???
}
val all = List(
GenericCounterfeiters.splitDoubleLetters,
singularCounterfeiter,
pluralCounterfeiter,
genderCounterfeiter(Neutral),
genderCounterfeiter(Masculine),
genderCounterfeiter(Feminine))
val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}
Now you can use Noun.nounCounterfeiter as your random Counterfeiter[Noun]. The main idea here is to have the same shape for each atomic counterfeiter and this is achieved by always passing the whole Word (or its subclass) to the method. So now each method has access to all the relevant information if it needs some.
If you prefer to move the typical if condition in counterfeiters to a single place, you may refactor your code to a bit more OOP-way:
class RandomCompoundCounterfeiter[W <: Word](val children: Seq[Counterfeiter[W]]) extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
Random.shuffle(children).takeWhile(c => {
c.counterfeit(word)
!word.isCounterfeited
})
}
}
trait SimpleCounterfeiter[-W <: Word] extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
if (isApplicable(word))
word.currentWriting = counterfeitImpl(word.correctWriting, word)
}
def isApplicable(word: W): Boolean
def counterfeitImpl(correctWriting: String, word: W): String
}
object GenericCounterfeiters {
val splitDoubleLetters = new SimpleCounterfeiter[Word] {
override def isApplicable(word: Word) = true
override def counterfeitImpl(correctWriting: String, word: Word) = ???
}
}
object Noun {
val singularCounterfeiter = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.number != Singular
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
val pluralCounterfeiter = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.number != Plural
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
def genderCounterfeiter(newGender: Gender) = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.gender != newGender
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
val all = List(
GenericCounterfeiters.splitDoubleLetters,
singularCounterfeiter,
pluralCounterfeiter,
genderCounterfeiter(Neutral),
genderCounterfeiter(Masculine),
genderCounterfeiter(Feminine))
val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}
It's been while Since I've started working on scala and I am wondering what kind of variable type is the best when I create a method which requires to return multiple data.
let's say If I have to make a method to get user info and it'll be called from many places.
def getUserParam(userId: String):Map[String,Any] = {
//do something
Map(
"isExist" -> true,
"userDataA" -> "String",
"userDataB" -> 1 // int
)
}
in this case, the result type is Map[String,Any] and since each param would be recognized as Any, You cannot pass the value to some other method requiring something spesifically.
def doSomething(foo: String){}
val foo = getUserParam("bar")
doSomething(foo("userDataA")) // type mismatch error
If I use Tuple, I can avoid that error, but I don't think it is easy to guess what each indexed number contains.
and of course there is a choice to use Case Class but once I use case class as a return type, I need to import the case class where ever I call the method.
What I want to ask is what is the best way to make a method returning more than 2 different variable type values.
Here are three options. Even though you might like the third option (using anonymous class) it's actually my least favorite. As you can see, it requires you to enable reflective calls (otherwise it throws a compilation warning). Scala will use reflection to achieve this which is not that great.
Personally, if there are only 2 values I use tuple. If there are more than two I will use a case class since it greatly improves code readability. The anonymous class option I knew it existed for a while, but I never used that it my code.
import java.util.Date
def returnTwoUsingTuple: (Date, String) = {
val date = new Date()
val str = "Hello world"
(date,str)
}
val tupleVer = returnTwoUsingTuple
println(tupleVer._1)
println(tupleVer._2)
case class Reply(date: Date, str: String)
def returnTwoUsingCaseClass: Reply = {
val date = new Date()
val str = "Hello world"
Reply(date,str)
}
val caseClassVer = returnTwoUsingCaseClass
println(caseClassVer.date)
println(caseClassVer.str)
import scala.language.reflectiveCalls
def returnTwoUsingAnonymousClass = {
val date = new Date()
val str = "Hello world"
new {
val getDate = date
val getStr = str
}
}
val anonClassVer = returnTwoUsingAnonymousClass
println(anonClassVer.getDate)
println(anonClassVer.getStr)
Sinse your logic with Map[String,Any] is more like for each key I have one of .. not for each key I have both ... more effective use in this case would be Either or even more effectively - scalaz.\/
scalaz.\/
import scalaz._
import scalaz.syntax.either._
def getUserParam(userId: String): Map[String, String \/ Int \/ Boolean] = {
//do something
Map(
"isExist" -> true.right,
"userDataA" -> "String".left.left,
"userDataB" -> 1.right.left
)
}
String \/ Int \/ Boolean is left-associatited to (String \/ Int) \/ Boolean
now you have
def doSomething(foo: String){}
unluckily it's the most complex case, if for example you had
def doSomethingB(foo: Boolean){}
you could've just
foo("userDataA").foreach(doSomethingB)
since the right value considered as correct so for String which is left to the left you could write
foo("userdata").swap.foreach(_.swap.foreach(doSomething))
Closed Family
Or you could craft you own simple type for large number of alternatives like
sealed trait Either3[+A, +B, +C] {
def ifFirst[T](action: A => T): Option[T] = None
def ifSecond[T](action: B => T): Option[T] = None
def ifThird[T](action: C => T): Option[T] = None
}
case class First[A](x: A) extends Either3[A, Nothing, Nothing] {
override def ifFirst[T](action: A => T): Option[T] = Some(action(x))
}
case class Second[A](x: A) extends Either3[Nothing, A, Nothing] {
override def ifSecond[T](action: A => T): Option[T] = Some(action(x))
}
case class Third[A](x: A) extends Either3[Nothing, Nothing, A] {
override def ifThird[T](action: A => T): Option[T] = Some(action(x))
}
now having
def getUserParam3(userId: String): Map[String, Either3[Boolean, String, Int]] = {
//do something
Map(
"isExist" -> First(true),
"userDataA" -> Second("String"),
"userDataB" -> Third(1)
)
}
val foo3 = getUserParam3("bar")
you can use your values as
foo3("userdata").ifSecond(doSomething)
Assume a basic case class like
case class Natural(i: Int)
Which is
class Natural(val i: Int)
object Natural {
def apply(i: Int): Natural = new Natural(i)
def unapply(n: Natural): Option[Int] = Some(n.i)
}
but I also have
def xxxx(i: Int): Option[Natural] = if (i > 0) Some(Natural(i)) else None
that is similar to unapply, but with reversed parameters (like apply). Another example:
class HttpMethod(val name: String)
object HttpMethod {
def apply(name: String): HttpMethod = new HttpMethod(name)
def unapply(method: HttpMethod): Option[String] = Some(method.name)
def xxxx(name: String): Option[HttpMethod] = name.toUpperCase match {
case "GET" => Some(HttpMethod("GET"))
case "PUT" => Some(HttpMethod("PUT"))
...
case _ => None
}
}
Is there any convention for the name of xxxx ? I thought from, but it could also be an overloaded unapply. If it were the latter you could do
val methodString: String = ???
methodString match {
case m # HttpMethod(name) => // use m...
}
(if it works, could fail because of overloading). But note that this isn't an extractor, but like a constructor, because it says: if this string can be an Http method, then build it and ... The real extractor is
val method: HttpMethod = ???
method match {
case HttpMethod(name) => // and now you have extracted the string name.
}
Of course it'd make more sense in a GADT with sealed trait, but the question isn't about this, but about the convention for xxxx
I have the following method:
def save(entity: A): Either[List[Error],A] + {....
which I want to test using specs2
I want to test for the existence of a specific error when a required field is not specified, like this:
val noNickname = User(
nickname = "",
name = "new name",
)
noNickname.save must beLeft.like {
case errors => {
atLeastOnceWhen(errors) {
case error => {
error.errorCode must equalTo(Error.REQUIRED)
error.field must equalTo("nickname")
}
}
}
}
It works fine, but I'd like to define my own matcher to make it less verbose, like this:
noNickname.save must haveError.like {
case error => {
error.errorCode must equalTo(Error.REQUIRED)
error.field must equalTo("nickname")
}
}
}
I had a look at the documentation (http://etorreborre.github.com/specs2/guide/org.specs2.guide.Matchers.html#Matchers) but I can't figure out how to define a custom matcher like haveError.like
Here your code with a few changes to make it compile:
case class Error(errorCode: String, field: String)
def save[A](entity: A): Either[List[Error],A] = Left(List(Error("REQUIRED", "nickname")))
case class User(nickname: String, name: String)
val noNickname = User(nickname = "", name = "new name")
"save noNickName" >> {
save(noNickname) must haveError.like {
case error => {
error.errorCode must equalTo("REQUIRED")
error.field must equalTo("nickname")
}
}
}
def haveError[T] = new ErrorMatcher[T]
class ErrorMatcher[T] extends Matcher[Either[List[T], _]] {
def apply[S <: Either[List[T], _]](value: Expectable[S]) =
result(value.value.left.toOption.isDefined,
value.description + " is Left",
value.description + " is not Left",
value)
def like[U](f: PartialFunction[T, MatchResult[U]]) =
this and partialMatcher(f)
private def partialMatcher[U](f: PartialFunction[T, MatchResult[U]]) =
new Matcher[Either[List[T], _]] {
def apply[S <: Either[List[T], _]](value: Expectable[S]) = {
// get should always work here because it comes after the "and"
val errors = value.value.left.toOption.get
val res = atLeastOnceWhen[T, U](errors)(f)
result(res.isSuccess,
value.description+" is Left[T] and "+res.message,
value.description+" is Left[T] but "+res.message,
value)
}
}
}
Notice that the matcher is defined on Either[List[T], _] everywhere.
I'm also wondering about the failure messages that are returned in case you don't find the expected error message, they might not be very explicit when the partial function fails.
So you may want to aim for using a contain matcher. Like this:
"save noNickName" >> {
save(noNickname) must haveError.containing(Error("REQUIRED", "nickname"))
}
// I'm reusing the beLeft matcher here
def haveError[T]: Matcher[Either[List[T], _]] = beLeft
// and using an implicit conversion to extend it
implicit def toErrorListMatcher[T](m: Matcher[Either[List[T], _]]): ErrorListMatcher[T] =
new ErrorListMatcher[T](m)
class ErrorListMatcher[T](m: Matcher[Either[List[T], _]]) {
def containing(t: T) =
// the 'contain' matcher is adapted to take in an
// Either[List[T], _] and work on its left part
m and contain(t) ^^ ((e: Either[List[T], _]) => e.left.toOption.get)
}
[Update]
The first solution (using atLeastOnceWhen and a partial function) can be combined with the second one (using an implicit) and the beLike matcher, to get maximum reusability of existing specs2 code:
def haveError[T]: Matcher[Either[List[T], _] = beLeft
implicit def toErrorListMatcher[T](m: Matcher[Either[List[T], _]]): ErrorListMatcher[T] =
new ErrorListMatcher[T](m)
class ErrorListMatcher[T](m: Matcher[Either[List[T], _]]) {
// beLike checks one element
// beLike.atLeastOnce transforms that matcher on a
// matcher on a sequence of elements
def like[S](f: PartialFunction[T, MatchResult[S]]) = {
m and beLike(f).atLeastOnce ^^ ((e: Either[List[T], _]) => e.left.toOption.get)
}
My game has
class Enemy
who's AI/functionality I can change with
trait Moving
trait VerticalMover extends Moving
trait RandomMover extends Moving
and so on. Now I need to fetch preloaded stuff based on trait. What I would like to do is have a Map that accepts all traits that extend Moving as keys and then some EnemyContainer as value that would have trait related content preloaded.
But how do I define such a Map and how do format my .get() to get the container by an instance of some Enemy. Something like:
val myEnemy = new Enemy with RandomMover
val myDetails:EnemyContainer = enemyDetailsStore.get(myEnemy.getClass)
Maybe you could wrap a Map[Manifest, Any] ensuring that the values corresponds to the manifest keys.
Possible sketch of that. First a little helper
class Typed[A](value: A)(implicit val key: Manifest[A]) {
def toPair: (Manifest[_], Any) = (key, value)
}
object Typed {
implicit def toTyped[A: Manifest](a: A) = new Typed(a)
implicit def toTypable[A](a: A) = new {
def typedAs[T >: A : Manifest] = new Typed[T](a)(manifest[T])
}
}
then the wrapper itself (which is not a map)
class TypedMap private(val inner: Map[Manifest[_], Any]) {
def +[A](t: Typed[A]) = new TypedMap(inner + t.toPair)
def +[A : Manifest](a: A) = new TypedMap(inner + (manifest[A] -> a))
def -[A : Manifest]() = new TypedMap(inner - manifest[A])
def apply[A : Manifest]: A = inner(manifest[A]).asInstanceOf[A]
def get[A : Manifest]: Option[A] = inner.get(manifest[A]).map(_.asInstanceOf[A])
override def toString = inner.toString
override def equals(other: Any) = other match {
case that: TypedMap => this.inner == that.inner
case _ => false
}
override def hashCode = inner.hashCode
}
object TypedMap {
val empty = new TypedMap(Map())
def apply(items: Typed[_]*) = new TypedMap(Map(items.map(_.toPair) : _*))
}
With that you can do
import Typed._
val repository = TypedMap("foo", 12, "bar".typedAs[Any])
repository: TypedMap = Map(java.lang.String -> foo, Int -> 12, Any ->
bar)
You retrieve elements with
repository[String] // returns "foo"
repository.get[Any] // returns Some("bar")
I think the private constructor should ensure that the _asInstanceOf is safe. inner may be left public, as it is immutable. This way, the rich interface of Map will be available, but unfortunately, not to create another TypedMap.
Well, I assume that your enemy details store is of type Map[Class[_ <: Moving], EnemyDetails]. I suspect that something like:
//gives a Map[Class[_ <: Moving], EnemyDetails] for all matching keys
enemyDetailsStore.filterKeys(_ isInstance myEnemy)
Or:
//Iterable[EnemyDetails]
enemyDetailsStore collect { case (c, d) if c isInstance myEnemy => d }
Or even just:
//Option[EnemyDetails]
enemyDetailsStore collectFirst { case (c, d) if c isInstance myEnemy => d }
Will do for you. The only "issue" with this code is that it's O(N), in that it requires a traversal of the map, rather than a simple lookup, which would be O(1), or O(log N)