1

我想创建一些简单的自定义字符串插补器,并且我成功了,只要我不尝试使用类型参数。使用StringContext的通用字符串插补器

import scala.concurrent.Future 

object StringImplicits { 
    implicit class FailureStringContext (val sc : StringContext) extends AnyVal { 

    // This WORKS, but it's specific to Future :( 

    def fail[T](args : Any*): Future[T] = { 
     val orig = sc.s (args : _*) 
     Future.exception[T](new Exception(orig)) 
    } 

    // I want this to work for Option,Try,Future!! 

    def fail[M,T](args:Any*): M[T] = { 
     val orig = sc.s (args : _*) 

     // Obviously does not work.. 
     M match { 
     case Future => Future.exception(new Exception(orig)) 
     case Option => None 
     case Try => Failure(new Exception(orig)) 
     case _ => ??? 
     } 
    } 
    } 
} 

我可以得到这个工作吗?我不能使用参数多态,因为我不是定义这三种类型的人。

这种伪代码模式匹配的类型级别的等价物是什么?

最新尝试

我最新的尝试是隐式地使用,但我没有这样的隐式!根据类型推断,我实际上有兴趣抓住一个类型,编译器要我返回

def fail[T, M[T]](args:Any*): M[T] = { 
    val orig = sc.s(args: _*) 

    implicitly[M[T]] match { 
    case _:Future[T] => Future.exception(new Exception(orig)) 
    case _ => ??? 
    } 
} 


<console>:18: error: could not find implicit value for parameter e: M[T] 
      implicitly[M[T]] match { 
        ^
<console>:19: error: value exception is not a member of object scala.concurrent.Future 
       case _: Future[T] => Future.exception(new Exception(orig)) 
             ^
+0

参见:'DEF试验[A,B](一个:任何):A [B] = ???'=>编译错误:'A'确实不采取类型参数。 'A'是通用的,例如可以是'Int'。 Int [String]不存在。所以编译器总是会抱怨。 –

+0

所以也许我需要告诉编译器,我想A是一个更高的类型?怎么样? – sscarduzio

+1

'def fail [T,M [T]](...)'让你分道扬part。但我不明白你将如何在呼叫站点指定M。您是否期望编译器根据所需的返回类型推断M使用的内容? –

回答

1

在我看来,最简单的方法是依靠良好的旧的重载:只需为每个类型定义一个不同的重载即可处理。

当然,现在有相同的签名有不同的重载问题,并像往常一样在scala中使用技巧来解决它们。在这里,我们将添加虚拟隐式参数来强制每个重载具有明显的签名。不漂亮,但它的工作原理,在这种情况下就足够了。

import scala.concurrent.Future 
import scala.util.{Try, Failure} 

implicit class FailureStringContext (val sc : StringContext) extends AnyVal { 

    def fail[T](args : Any*): Future[T] = { 
    Future.failed[T](new Exception(sc.s (args : _*))) 
    } 

    def fail[T](args : Any*)(implicit dummy: DummyImplicit): Option[T] = { 
    Option.empty[T] 
    } 

    def fail[T](args : Any*)(implicit dummy: DummyImplicit, dummy2: DummyImplicit): Try[T] = { 
    Failure[T](new Exception(sc.s (args : _*))) 
    } 
} 

和多田:

scala> fail"oops": Option[String] 
res6: Option[String] = None 

scala> fail"oops": Future[String] 
res7: scala.concurrent.Future[String] = [email protected] 

scala> fail"oops": Try[String] 
res8: scala.util.Try[String] = Failure(java.lang.Exception: oops) 
+0

整洁,但是当你调用它的时候需要指定类型,而首先解决了重载的问题,在我看来。只需调用'失败'函数不同的名称 - 'failOption'等。 –

+0

有没有办法绕过它,你必须以某种方式给予预期的类型。但是,对于任何类型的归属,只有当您不在预期类型已知的情况下才有必要。如果您使用'fail'作为具有显式返回类型的方法的返回值),或者作为参数传递给非泛型方法,则不需要再指定类型。 –

+0

而且,引人注意的是,只是指定你想要的类型比执行手动名称更为简洁... –