Cloud Haskell - 如何为闭包编写 "pure"?

Cloud Haskell - How to write "pure" for Closures?

我一直在玩 Cloud Haskell. I've noticed in the hackage documentation 有一种应用程序界面。但特别是我正在尝试查找或编写具有以下签名的函数 closurePure

closurePure :: (Typeable a, Binary a) => a -> Closure a

这基本上是pure的限制版。

虽然 Closure 数据类型本身是抽象的,但以下 closure 提供:

closure :: Static (ByteString -> a) -> ByteString -> Closure a

所以我可以做到这一点:

closurePure :: (Typeable a, Binary a) => a -> Closure a
closurePure x = closure ??? (encode x)

问题是在 ??? 所在的位置放什么。

我的第一次尝试如下:

myDecode :: (Typeable a, Binary a) => Static (ByteString -> a)
myDecode = staticPtr (static decode)

但阅读 GHC docs on static pointers, the show example suggested to me that you can't have a constraint because a constrained function doesn't have a Typeable instance. So I tried the work around suggested using Dict:

myDecode :: Typeable a => Static (Dict (Binary a) -> ByteString -> a)
myDecode = staticPtr (static (\Dict -> decode))

但现在我得到了不适合上面 closure 函数的错误类型。

有没有写 closurePure 或类似的东西(或者我在 Cloud Haskell 文档中错过了它)?将 binary 普通类型提升到 Closures 似乎对于使用给定的应用程序接口至关重要,但我不知道如何去做。

请注意,我可以这样做:

class StaticDecode a where
  staticPtrDecode :: StaticPtr (ByteString -> a)

instance StaticDecode Int where
  staticPtrDecode = static Data.Binary.decode

instance StaticDecode Float where
  staticPtrDecode = static Data.Binary.decode

instance StaticDecode Integer where
  staticPtrDecode = static Data.Binary.decode

-- More instances etc...

myPure :: forall a. (Typeable a, StaticDecode a, Binary a) => a -> Closure a
myPure x = closure (staticPtr staticPtrDecode) (encode x)

效果很好,但基本上需要我为每个 Binary 实例重复一个实例。看起来很乱,我更喜欢另一种方式。

让我们花点时间考虑一下您的要求。回想一下,typeclasses 基本上是 shorthand 用于字典传递。所以让我们重写:

data BinaryDict a = BinaryDict 
  { bdEncode :: a -> ByteString
  , bdDecode :: ByteString -> a
  }

现在你想写一个函数:

closurePure :: (Typeable a) => BinaryDict a -> a -> Closure a

您的尝试是:

closurePure bdict = closure (staticPtr (static (bdDecode bdict))) . bdEncode bdict

现在我们可以清楚地看到发生了什么,我们可以看到 static 的论点无法结束。如果 BinaryDicts 被允许随意创建,比如说从用户数据,这个功能将是不可能的。我们需要:

closurePure :: (Typeable a) => Static (BinaryDict a) -> a -> Closure a

也就是说,我们需要静态指针 table 中所需 Binary 个实例的条目。因此您的枚举解决方案,以及为什么我怀疑需要这样的解决方案。我们也不能指望自动枚举它,因为有无限多的实例。

然而,这对我来说似乎很愚蠢,因为实例似乎正是您希望自动成为静态的那种东西。它们本质上是静态的(那是什么,reflection?我听不见你说话)。这可能至少在分发的 Haskell 论文中被反思过(我没有读过)。

我们通常可以通过简单地创建一个 class 来解决这个问题,该 class 具体枚举每个 class 的每个实例(déjà vu?)。

class c => StaticConstraint c where
    staticConstraint :: StaticPtr (Dict c)
instance StaticConstraint (Show Int) where
    staticConstraint = static Dict
-- a handful more lines...

严肃一点,如果你真的不想枚举(我不怪你),你至少可以通过调用约定来减轻痛苦:

closurePure :: (Typeable a, Binary a) => StaticPtr (ByteString -> a) -> a -> Closure a
closurePure decodePtr = closure (staticPtr decodePtr) . encode

someClosure :: Closure Int
someClosure = closurePure (static decode) 42

这个废话是必要的,因为 static 是一个 "syntactic form" 而不是一个函数——通过提及它,我们表明 IntBinary 实例实际上必须生成并记录在静态指针table.

如果你觉得厚脸皮,你可以启用 {-# LANGUAGE CPP #-}

-- PURE :: (Binary a, Typeable a) => a -> Closure a, I promise
#define PURE (closurePure (static decode))

someClosure :: Closure Int
someClosure = PURE 42

也许有一天 Haskell 会迈出下一步,升级到经过时间考验的 Segmentation fault (core dumped) 它的前辈,而不是吐出那些傲慢的类型错误。

你说得对,Closure 有一个应用性的- 结构,这一事实在 [=29= 的接口和实现中变得更加明确].它不太适用,因为在 pure 的情况下,我们确实有额外的约束,即参数必须以某种方式可序列化。

其实我们有更强的约束。不仅参数必须是可序列化的,约束本身也必须是可序列化的。就像很难直接序列化函数一样,你可以想象很难序列化约束。但就像函数一样,诀窍是将 静态指针 序列化到约束本身,如果存在这样的静态指针的话。我们怎么知道这样的指针存在呢?我们可以引入一个类型 class,在给定约束的情况下,用一个方法为我们提供指针的名称:

class GimmeStaticPtr c where
  gimmeStaticPtr :: StaticPtr (Dict c)

这里有一个小技巧。 StaticPtr 的类型索引的种类是 * 的种类,而约束的种类是 Constraint。因此,我们重用了 constraints 库中的一个技巧,该技巧包括将约束包装到数据类型(上面的 Dict)中,就像所有数据类型一样,它属于 * 类型。具有关联 GimmeStaticPtr 实例的约束称为 静态约束

一般来说,有时组合静态约束以获得更多静态约束很有用。 StaticPtr 不可组合,但 Closure 是。所以 distributed-closure 实际上做的是定义一个类似的 class,我们称之为

class GimmeClosure c where
  gimmeClosure :: Closure (Dict c)   

现在我们可以用与您类似的方式定义 closurePure

closurePure :: (Typeable a, GimmeClosure (Binary a)) => a -> Closure a

如果将来编译器可以通过根据需要生成静态指针来即时解析 GimmeClosure 约束,那就太好了。但就目前而言,最接近的是模板 Haskell。分布式闭包提供了一个模块,可以在 class Cls 的定义站点自动生成 GimmeClosure (Cls a) 约束。参见 withStatic here

顺便说一句,Edsko de Vries 给出了 great talk 关于分布式闭包及其中体现的想法。