解密最棘手的scala方法原型之一(光滑)

mat*_*ter 8 scala slick-2.0

<>http://slick.typesafe.com/doc/2.1.0/api/index.html#scala.slick.lifted.ToShapedValue看下面scala slick类中的方法,它让我想起那个标志性的stackoverflow线程关于scala原型.

def <>[R, U](f: (U) ? R, g: (R) ? Option[U])
(implicit arg0: ClassTag[R], shape: Shape[_ <: FlatShapeLevel, T, U, _]):
MappedProjection[R, U]
Run Code Online (Sandbox Code Playgroud)

有人大胆且知识渊博,可以提供这个长原型定义的明确演练,仔细澄清所有类型的协方差/不变性,双参数列表和其他高级scala方面吗?

这个练习也将极大地帮助处理类似复杂的原型!

lmm*_*lmm 20

好的,我们来看看:

class ToShapedValue[T](val value: T) extends AnyVal {
  ...
  @inline def <>[R: ClassTag, U](f: (U) ? R, g: (R) ? Option[U])(implicit shape: Shape[_ <: FlatShapeLevel, T, U, _]): MappedProjection[R, U]
}
Run Code Online (Sandbox Code Playgroud)

这个类是一个AnyVal包装器; 虽然我实际上看不到implicit从快速查看的转换,它闻起来像"皮条客我的图书馆"模式.所以我猜这是为了将<>"扩展方法"添加到某些(或者所有)类型上.

@inline是一种注释,一种将元数据放在任何东西上的方法; 这个是对编译器的暗示,应该内联.<>是方法名称 - 许多看起来像"运算符"的东西只是scala中的普通方法.

你链接的文档已经扩展R: ClassTag到普通Rimplicit ClassTag[R]- 这是一个"上下文绑定",它只是语法糖.ClassTag是一个编译器生成的东西,它存在于每个(具体)类型并有助于反射,所以这是一个提示,该方法可能会R在某个时候对某个方面做一些反思.

现在,肉:这是一个通用方法,由两种类型参数化:[R, U].它的论点是两个函数,f: U => Rg: R => Option[U].这看起来有点像功能Prism概念 - 转换UR始终有效,转换RU有时不起作用.

签名(有点)的有趣部分是implicit shape最后的.Shape被描述为一个"类型类",所以这可能是作为一个"约束"最好的思想:它限制了可能的类型UR我们可以调用这个函数,只有那些对于其适当Shape可用.

纵观对于文档Shape,我们可以看到四种类型为Level,Mixed,UnpackedPacked.所以约束是:必须有一个Shape,其"级别"是某个子类型FlatShapeLevel,其中Mixed类型是T,Unpacked类型是R(Packed类型可以是任何类型).

所以,这是一个类型级函数,表示R"解压缩版本" T.使用来自例子Shape再次说明文件,如果T(Column[Int], Column[(Int, String)], (Int, Option[Double]))那么R(Int, (Int, String), (Int, Option[Double])(和它仅适用于FlatShapeLevel,但我要做一个判断调用,这可能并不重要).U有趣的是,完全不受约束.

因此,通过在两个方向上提供转换功能,我们可以创建MappedProjection[unpacked-version-of-T, U]任意一个T.因此,在一个简单的版本中,可能TColumn[String]- String数据库中列的表示- 我们希望将其表示为某些特定于应用程序的类型,例如EmailAddress.所以R=String,U=EmailAddress我们在两个方向都提供转换功能:f: EmailAddress => Stringg: String => Option[EmailAddress].这是有道理的,它的周围是这样的:每一个EmailAddress可以表示为一个String(至少,他们最好是,如果我们希望能够将它们存储在数据库中),但并不是每一个String是有效的EmailAddress.如果我们的数据库在电子邮件地址栏中以某种方式具有" http://www.foo.com/ ",我们g将返回None,并且Slick可以优雅地处理此问题.

MappedProjection遗憾的是,它本身就是无证的.但我猜它是我们可以查询的东西的某种懒惰表示; 我们有一个Column[String],现在我们有一个伪列的东西,其(基础)类型是EmailAddress.所以这可能让我们写伪查询,如"从用户选择,其中emailAddress.domain ='gmail.com’",这将是不可能直接在数据库做(不知道哪一个电子邮件地址的一部分域名),但很容易在代码的帮助下完成.至少,这是我对其可能做的最好的猜测.

可以说,通过使用标准Prism类型(例如Monocle中的类型)而不是明确地传递一对函数,可以使函数更清晰.使用隐式提供类型级函数是尴尬但必要的; 在完全依赖类型的语言(例如Idris)中,我们可以将类型级函数编写为函数(类似def unpackedType(t: Type): Type = ...).从概念上讲,这个函数看起来像:

def <>[U](p: Prism[U, unpackedType(T)]): MappedProjection[unpackedType(T), U]
Run Code Online (Sandbox Code Playgroud)

希望这能解释一些阅读新的,不熟悉的功能的思考过程.我根本不知道Slick,所以我不知道我对它的<>用途有多准确- 我做对了吗?