8

我在媒体上看到了这篇文章: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]
    }
  }
}

以下是我的问题:

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

谢谢!

更新:

第一个问题:

根据规范,当上限缺失时,假定为 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益智游戏

4

1 回答 1

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 }
    
    但是,我们不必将其指定为具体类型

所以问题中的代码

    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?一种方法是使用从到的自然转换(或~>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 = ...
})

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

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

Capture[F](f: F[Arbitrary] => Arbitrary): Capture[F]

F[A] => 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 */ }
于 2019-05-24T08:24:17.913 回答