我在媒体上遇到了这篇文章:https://medium.com/@odomontois/tagless-unions-inions-in-scala-scala-2-12-555ab0100c2ff。我很难理解一块代码。本文的完整源代码可以在此处找到:https://github.com/odomontois/zio-tagless-err。
代码是这样:
trait Capture[-F[_]] {
def continue[A](k: F[A]): A
}
object Capture {
type Constructors[F[_]] = F[Capture[F]]
type Arbitrary
def apply[F[_]] = new Apply[F]
class Apply[F[_]] {
def apply(f: F[Arbitrary] => Arbitrary): Capture[F] = new Capture[F] {
def continue[A](k: F[A]): A = f(k.asInstanceOf[F[Arbitrary]]).asInstanceOf[A]
}
}
}
这是我的问题:
- 鉴于对象中该类型声明的类型,Scala编译器如何求解/处理任意类型?这似乎取决于应用方法参数类型,但是该正方形如何与捕获是对象的事实,并且您可以使用多个具有不同类型的应用程序呢?我遇到了这篇文章,在对象中没有定义没有定义的类型声明的含义是什么?但这对我来说还不清楚。
- 根据文章,上面的代码使用另一个库中的技巧https://github.com/alexknvl。您能解释一下这种模式背后的想法吗?这是为了什么?我了解作者使用它来捕获登录过程中可能发生的多种错误。
谢谢!
更新:
第一个问题:
基于规格,当缺少上限时,它被假定为任何。因此,任意被视为任何事物,但它似乎与任何互动都无法互换。
此编译:
object Test {
type Arbitrary
def test(x: Any): Arbitrary = x.asInstanceOf[Arbitrary]
}
但是,这不是:
object Test {
type Arbitrary
def test(x: Any): Arbitrary = x
}
Error:(58, 35) type mismatch;
found : x.type (with underlying type Any)
required: Test.Arbitrary
def test(x: Any): Arbitrary = x
另请参阅此Scala uzzer。
这有点晦涩,尽管使用类型别名的法律用法。在规范中,您可以阅读类型的别名可以用来参考某种抽象类型并用作类型的约束,这表明编译器应允许。
-
type X >: L <: U
意味着X
无论是什么,都应在L
和G
之间绑定 - 实际上我们知道的任何值都可以使用该定义, -
type X = Y
是非常精确的约束 - 编译器知道每次我们都可以称其为y,反之亦然 - 但是
type X
也是合法的。我们通常使用它在trait
或其他内容中声明它,但是随后我们在扩展类中对其进行了更多约束。
但是,我们不必将其指定为具体类型。trait TestA { type X } trait TestB extends TestA { type X = String }
因此,问题的代码
def apply(f: F[Arbitrary] => Arbitrary): Capture[F] = new Capture[F] {
def continue[A](k: F[A]): A = f(k.asInstanceOf[F[Arbitrary]]).asInstanceOf[A]
}
可以读为:我们有Arbitrary
类型,我们一无所知,但是我们知道,如果将F[Arbitrary]
放入函数,我们将获得Arbitrary
。
的事情是,编译器不会让您将任何值作为Arbitrary
传递,因为它无法证明您的值是此类型的。如果可以证明Arbitrary=A
您可以写:
def apply(f: F[Arbitrary] => Arbitrary): Capture[F] = new Capture[F] {
def continue[A](k: F[A]): A = f(k)
}
但是,它不能,这就是为什么您被迫使用.asInstanceOf
的原因。这就是为什么type X
不等于说type X = Any
。
有一个原因,为什么我们不仅仅是使用仿制药。您如何在内部传递多态功能?一个F[A] => A
适用于任何A
?一种方法是使用从F[_]
到Id[_]
的自然转换(或~>
或FunctionK
(。但是使用它会多么混乱!
// no capture pattern or other utilities
new Capture[F] {
def continue[A](fa: F[A]): A = ...
}
// using FunctionK
object Capture {
def apply[F[_]](fk: FunctionK[F, Id]): Caputure[F] = new Capture[F] {
def continue[A](fa: F[A]): A = fk(fa)
}
}
Capture[F](new FunctionK[F, Id] {
def apply[A](fa: F[A]): A = ...
})
不愉快。问题是,您不能传递诸如多态函数(此处[A]: F[A] => A
(之类的东西。您只能使用多态方法传递实例(即FunctionK
起作用(。
因此,我们是通过将单型函数传递到固定到无法实例化的类型(Arbitrary
(的A
的单体函数来进行黑客入侵,因为没有类型的编译器可以证明它与Arbitrary
匹配。
Capture[F](f: F[Arbitrary] => Arbitrary): Capture[F]
然后,您正在迫使编译器认为学习A
时是F[A] => A
类型。
f(k.asInstanceOf[F[Arbitrary]]).asInstanceOf[A]
模式的另一部分是类型参数的部分应用。如果您一次做事:
object Capture {
type Constructors[F[_]] = F[Capture[F]]
type Arbitrary
def apply[F[_]](f: F[Arbitrary] => Arbitrary): Capture[F] = new Capture[F] {
def continue[A](k: F[A]): A = f(k.asInstanceOf[F[Arbitrary]]).asInstanceOf[A]
}
}
您将遇到一些问题将Capture.apply
作为正常函数传递。您将不得不做otherFunction(Capture[F](_))
之类的事情。通过创建Apply
"工厂",我们可以拆分键入参数应用程序并传递F[Arbitrary] => Arbitrary
函数。
长话短说,这都是让您写的:
-
takeAsParameter(Capture[F])
和 -
Capture[F] { fa => /* a */ }