使用代数验证和捕获错误
Validation and capturing errors using an algebra
我在媒体上看到这篇文章:https://medium.com/@odomontois/tagless-unions-in-scala-2-12-55ab0100c2ff. There is a piece of code that I have a hard time understanding. The full source code for the article can be found here: 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 编译器如何 solve/handle 假定类型是在对象中声明的任意类型?它似乎取决于 apply 方法参数类型,但是这如何与 Capture 是一个对象并且您可以有多个不同类型的 apply 调用这一事实相吻合?我遇到了这个 post What is the meaning of a type declaration without definition in an object? 但我还是不清楚。
- 根据文章,上面的代码使用了来自另一个库的技巧 https://github.com/alexknvl。您能否解释一下这种模式背后的想法是什么?它是做什么用的?据了解,作者使用它是为了捕获登录过程中可能发生的多种类型的错误。
谢谢!
更新:
第一个问题:
基于 spec 当缺少上限时,假定为 Any。因此,Arbitrary 被视为 Any,但是,它似乎不能与 Any 互换。
这样编译:
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 puzzler。
这有点晦涩,但类型别名的合法使用。在规范中,您 can read 类型别名可能用于引用某些抽象类型并用作类型约束,建议编译器应该允许什么。
type X >: L <: U
意味着 X
,无论它是什么,都应该被限制在 L
和 G
之间——实际上我们知道满足该定义的任何值都可以在那里使用,
type X = Y
是非常精确的约束——编译器知道每次我们有 Y 我们都可以调用它 Y,反之亦然
- 但
type X
也是合法的。我们通常使用它来在 trait
或其他东西中声明它,但随后我们在扩展 class 时对其施加更多限制。
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
.
我们不简单地使用泛型是有原因的。怎么在里面传入一个polymorphic函数呢?一个对任何 A
做 F[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
有效)。
所以我们通过将 A
固定为无法实例化的类型 (Arbitrary
) 的 monomorphoc 函数传递来解决这个问题,因为没有类型编译器可以证明它匹配 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
"factory" 我们可以拆分类型参数应用程序并传递 F[Arbitrary] => Arbitrary
函数。
长话短说,就是让你写:
takeAsParameter(Capture[F])
和
Capture[F] { fa => /* a */ }
我在媒体上看到这篇文章:https://medium.com/@odomontois/tagless-unions-in-scala-2-12-55ab0100c2ff. There is a piece of code that I have a hard time understanding. The full source code for the article can be found here: 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 编译器如何 solve/handle 假定类型是在对象中声明的任意类型?它似乎取决于 apply 方法参数类型,但是这如何与 Capture 是一个对象并且您可以有多个不同类型的 apply 调用这一事实相吻合?我遇到了这个 post What is the meaning of a type declaration without definition in an object? 但我还是不清楚。
- 根据文章,上面的代码使用了来自另一个库的技巧 https://github.com/alexknvl。您能否解释一下这种模式背后的想法是什么?它是做什么用的?据了解,作者使用它是为了捕获登录过程中可能发生的多种类型的错误。
谢谢!
更新:
第一个问题:
基于 spec 当缺少上限时,假定为 Any。因此,Arbitrary 被视为 Any,但是,它似乎不能与 Any 互换。
这样编译:
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 puzzler。
这有点晦涩,但类型别名的合法使用。在规范中,您 can read 类型别名可能用于引用某些抽象类型并用作类型约束,建议编译器应该允许什么。
type X >: L <: U
意味着X
,无论它是什么,都应该被限制在L
和G
之间——实际上我们知道满足该定义的任何值都可以在那里使用,type X = Y
是非常精确的约束——编译器知道每次我们有 Y 我们都可以调用它 Y,反之亦然- 但
type X
也是合法的。我们通常使用它来在trait
或其他东西中声明它,但随后我们在扩展 class 时对其施加更多限制。
然而,我们不必将其指定为具体类型。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
.
我们不简单地使用泛型是有原因的。怎么在里面传入一个polymorphic函数呢?一个对任何 A
做 F[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
有效)。
所以我们通过将 A
固定为无法实例化的类型 (Arbitrary
) 的 monomorphoc 函数传递来解决这个问题,因为没有类型编译器可以证明它匹配 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
"factory" 我们可以拆分类型参数应用程序并传递 F[Arbitrary] => Arbitrary
函数。
长话短说,就是让你写:
takeAsParameter(Capture[F])
和Capture[F] { fa => /* a */ }