使用代数验证和捕获错误

cos*_*sta 8 scala tagless-final zio

我在以下媒体上看到了这篇文章:https : //medium.com/@odomontois/tagless-unions-in-scala-2-12-55ab0100c2ff。我很难理解其中的一段代码。文章的完整源代码可以在这里找到: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]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的问题:

  • 假定类型在对象中声明,scala编译器如何解决/处理任意类型?它似乎取决于apply方法的参数类型,但是那又如何与Capture是一个对象并且您可以使用不同类型的多个apply调用这一事实相吻合呢?我碰到过这篇文章,在对象中没有定义的类型声明是什么意思?但我仍然不清楚。
  • 根据文章,上面的代码使用了另一个库https://github.com/alexknvl的技巧。您能否解释一下这种模式背后的想法?这是为了什么 我了解作者使用它是为了捕获在登录过程中可能发生的多种类型的错误。

谢谢!

更新:

第一个问题:

根据规格,如果缺少上限,则假定为“任意”。因此,任意处理被视为任意,但是,它似乎无法与任意互换。

这样编译:

object Test {
    type Arbitrary

    def test(x: Any): Arbitrary = x.asInstanceOf[Arbitrary]
  }
Run Code Online (Sandbox Code Playgroud)

但是,这不是:

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
Run Code Online (Sandbox Code Playgroud)

另请参阅此斯卡拉拼图游戏

Mat*_*zok 3

虽然类型别名的合法用法有点晦涩难懂。在规范中,您可以阅读类型别名可能用于引用某些抽象类型并用作类型约束,建议编译器应该允许什么。

  • type X >: L <: U意味着X,无论它是什么,都应该绑定在L和之间G- 实际上我们知道满足该定义的任何值都可以在那里使用,
  • type X = Y是非常精确的约束 - 编译器知道每次我们有 Y 时我们都可以将其称为 Y,反之亦然
  • type X也是合法的。我们通常使用它来声明它trait或其他东西,但随后我们在扩展类中对其施加更多限制。
    trait TestA { type X }
    trait TestB extends TestA { type X = String }
    
    Run Code Online (Sandbox Code Playgroud) 但是,我们不必将其指定为具体类型

所以问题中的代码

    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]
    }
Run Code Online (Sandbox Code Playgroud)

可以理解为:我们有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)
    }
Run Code Online (Sandbox Code Playgroud)

然而,它不能,这就是为什么你被迫使用.asInstanceOf. 这就是为什么type X不等于说type X = Any

但我们不简单地使用泛型是有原因的。如何在内部传递多态函数?一个对F[A] => A任何人都有效的A?一种方法是使用从到 的自然变换(或~>FunctionK)。但使用起来会多么混乱啊!F[_]Id[_]

// 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 = ...
})
Run Code Online (Sandbox Code Playgroud)

不愉快。问题是,你不能传递诸如多态函数之类的东西(此处[A]: F[A] => A)。您只能使用多态方法传递实例(这是FunctionK有效的)。

因此,我们通过将固定的单态函数传递给A无法实例化的类型 ( Arbitrary) 来解决这个问题,因为没有类型编译器可以证明它匹配Arbitrary

Capture[F](f: F[Arbitrary] => Arbitrary): Capture[F]
Run Code Online (Sandbox Code Playgroud)

F[A] => A然后,当您学习时,您将迫使编译器认为它是类型A

f(k.asInstanceOf[F[Arbitrary]]).asInstanceOf[A]
Run Code Online (Sandbox Code Playgroud)

该模式的另一部分是排序类型参数的部分应用。如果你一口气做完一件事:

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]
    }
}
Run Code Online (Sandbox Code Playgroud)

Capture.apply例如,作为普通函数传递时,您会遇到一些问题。你必须做类似的事情otherFunction(Capture[F](_))。通过创建Apply“工厂”,我们可以拆分类型参数应用并传递函数F[Arbitrary] => Arbitrary

长话短说,这一切都是为了让你写:

  • takeAsParameter(Capture[F])
  • Capture[F] { fa => /* a */ }