使用代数验证和捕获错误

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]
    }
  }
}

这是我的问题:

谢谢!

更新:

第一个问题:

基于 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,无论它是什么,都应该被限制在 LG 之间——实际上我们知道满足该定义的任何值都可以在那里使用,
  • 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函数呢?一个对任何 AF[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 */ }