考虑以下组合器用于包装有效计算的仿函数:
def bracket[F[_]: Effect, A, R](alloc: F[R])(use: R => F[A], release: R => F[Unit]): F[A]
可以假设 Effect
是有效仿函数的合适类型类。 Effect
的确切实施无关紧要,但举一个具体的例子,请考虑https://github.com/typelevel/cats-effect
cats.effect.IO
该组合器旨在安全地对use
的评估进行排序和释放外部资源R
,这可能会产生大量的创建,破坏或维护成本。如果use
表示运行时故障,则不评估alloc
。无论release
中表达的任何运行时故障,都会评估use
。在评估R
之后,release
的剩余可寻址的延迟实例被认为是完全无法使用的,并且代表了已释放的资源。
正如这里所写,bracket
组合器是不安全的。考虑:
/** Holds a real world mutable and/or expensive resource. */
type OpenThing
def allocExpensive[F[_]: Effect]: F[OpenThing] = ???
def releaseRuntime(ot: OpenThing): F[Unit] = ???
def leaks[F[_]: Effect] =
bracket[F, R, R](allocExpensive[F])
((r: R) => Effect[F].pure(r),
(r: R) => releaseRuntime(r))
def breaks[F[_]: Effect]: F[Doom] =
for {
deadRef <- leaks[F]
} yield deadRef.totallyIllegal
此示例编码非常直接的不安全用法,但显然可能有更微妙的版本,其中A =:= R
不成立,但A
嵌入,关闭或以其他方式维护指向死资源句柄的链接在R
内找到。
我的问题:
有没有办法更改bracket
的签名以防止泄露R
的实例?
或者如果使用bracket
类似于:
def withOpenThing[F[_], A](job: OpenThing => F[A]): F[A] =
bracket[F, A, OpenThing](allocExpensive[F])(job, releaseRuntime)
是否有一种构建OpenThing的方法可以阻止它在A
中被引用?
整个努力对我来说似乎是一个很长的镜头,但Scala类型系统中的东西比我虚弱想象中的梦想更多。