2017-07-25 74 views
2

有了這個類型的類用於將映射到一個案例類創建一個泛型類的函數被用作toCaseClass[User](aUserMap) // returns UserHOWTO使用仿函數

但我也希望能夠通過Option [Map []]或Future [Map []]或List [Map []]使用此功能。 所以我實現了用仿函數像這樣的通用功能:

def toCaseClass[T <: Product, F[_]: cats.Functor](fOfMaps: F[Map[String, Any]])(implicit mapper: ToCaseClassMapper[T]): F[T] = { 
    cats.Functor[F].map(fOfMaps)(map => toCaseClass(map)(mapper)) 
} 

但現在這個功能已經被用作toCaseClass[User,List](listOfUserMaps) // returns List[User]

不過,我想能夠使用的功能

toCaseClass[User](listOfMaps) 
toCaseClass[User](futureOfMap) 
toCaseClass[User](optionOfMap) 

,而無需指定仿函數類型。 這是可能的嗎?
可以無形的懶惰來解決這個問題嗎?

編輯:解決方案
感謝@ Jasper-m和@ dk14的答案。 所以解決這個問題的'技巧'是在Functor類型之前的類中首先捕獲'T'類型。我喜歡@'Jasper-m解決方案與'apply'方法,因爲這會保持語法幾乎與以前相似。
雖然我做了一些調整。由於已經有'ToCaseClassMapper'類,它也捕獲'T'類型,我決定將它與'ToCaseClass'類結合起來。此外,使用@ Jasper-m的方法,當使用'toCaseClass'函數映射某個值時,如Option(value).map(toCaseClass),當值爲Map或List [Map]時,toCaseClass的用法必須不同。

我的解決方案如下內容:

@implicitNotFound("Missing ToCaseClassMapper implementation for type ${T}") 
trait ToCaseClassMapper[T <: Product] { 
    def toCaseClass(map: Map[String, Any]): T 

    import scala.language.higherKinds 

    def toCaseClass[F[_]: cats.Functor, A](fOfMaps: F[Map[String, A]]): F[T] = { 
    cats.Functor[F].map(fOfMaps)(toCaseClass) 
    } 
} 

由於使用了toCaseClass功能,其中ToCaseClassMapper實例已經隱含可用,我決定扔掉功能,只是與mapper.toCaseClass(_)取代它。這清理了一些不需要的代碼,現在使用映射器的語法是相同的,無論該值是Map還是Option,List,Future(或任何其他Functor)。

回答

1

目前在Scala中不可能有一個顯式提供的類型參數,並且可以推斷出另一個類型參數在同一個類型參數列表中,也不可能有一個方法的多個類型參數列表。解決方法是創建一個助手類並分兩個階段拆分方法調用:首先創建助手類的實例,然後在該對象上調用apply方法。

class ToCaseClass[T <: Product] { 
    def apply[F[_]: cats.Functor, A](fOfMaps: F[Map[String, A]])(implicit mapper: ToCaseClassMapper[T]): F[T] = { 
    cats.Functor[F].map(fOfMaps)(map => toCaseClass(map)(mapper)) 
    } 
} 

def toCaseClass[T <: Product] = new ToCaseClass[T] 
def toCaseClass[T <: Product](map: Map[String, Any])(implicit mapper: ToCaseClassMapper[T]): T = { 
    mapper.toCaseClass(map) 
} 

toCaseClass[User](listOfMaps) 
toCaseClass[User](futureOfMap) 
toCaseClass[User](optionOfMap) 

編輯:正如DK14指出,還有一類推理問題就在這裏,在這裏F被推斷爲Any。我不知道是什麼原因造成的,但我認爲這是與這種模式解決的問題不同的正交問題。

編輯2:我想通了。這是因爲F在其類型參數中是不變的。 F[Map[String, String]]不是F[Map[String, Any]]的子類型,所以編譯器做了一些奇怪的事情,推斷FAny。解決方法是將類型參數A而不是Any,或使用存在類型Map[String,_]

+0

您使用哪個版本的scala來檢查它?我的2.11.8給出:'找不到所有類型cat的證據參數的隱式值。函數[Any]' – dk14

+0

並不是所有的代碼都提供了,因此測試它並不是很容易,但現在我嘗試了它,那個錯誤。但在我看來,這與問題是正交的。 –

1

這工作:

class Mapper[T <: Product](implicit val mapper: ToCaseClassMapper[T]){ 
    def toCaseClass[F[_]: cats.Functor, Z <: Map[String, Any]](fOfMaps: F[Z]): F[T] = { 
    cats.Functor[F].map(fOfMaps)(map => mapper.toCaseClass(map)) 
    } 
} 

object Mapper{ 
    def apply[T <: Product: ToCaseClassMapper] = new Mapper[T]{} 
} 

import cats.implicits._ 

Mapper[User].toCaseClass(List(Map("aaa" -> 0))) 

除了明顯的引進類(以分體式參數)一些技巧使用,以及:

1)移動mapper到構造函數,因此它可以被首先解決(不知道它幫助)

2)什麼明確的幫助是引入Z <: Map[String, Any],否則scala(至少我的舊版本2.11.8)會推斷F[_]Any由於某種原因

P.S.您可以使用apply而不是toCaseClass - 它會縮短語法