我一直在玩Cloud Haskell。我注意到in the hackage documentation有一种应用界面。但特别是我正在尝试使用以下签名来查找或编写函数closurePure
:
closurePure :: (Typeable a, Binary a) => a -> Closure a
这基本上是纯粹的限制版本。
虽然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后,show
示例向我建议您不能有约束,因为受约束的函数没有Typeable
实例。所以我尝试了使用Dict
建议的工作:
myDecode :: Typeable a => Static (Dict (Binary a) -> ByteString -> a)
myDecode = staticPtr (static (\Dict -> decode))
但是现在我的错误类型不适合上面的closure
函数。
有没有写closurePure
或类似的东西(或者我在Cloud Haskell文档中错过了它)?将binary
普通类型提升为Closure
似乎对使用给定的应用界面至关重要,但我无法弄清楚如何做到这一点。
请注意,我可以这样做:
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
实例重复一个实例。看起来很乱,我更喜欢另一种方式。
答案 0 :(得分:4)
你是对的,Closure
有一个applicative- like 结构,这个事实在distributed-closure的界面和实现中都更加明确。它不是很适用,因为在pure
情况下我们确实有额外的约束,参数必须以某种方式可序列化。
实际上,我们有更强的约束力。参数不仅必须是可序列化的,而且约束本身必须是可序列化的。就像直接序列化函数一样,你可以想象很难序列化约束。但就像函数一样,诀窍是将静态指针序列化到约束本身,如果这样的静态指针存在的话。我们怎么知道这样的指针存在?我们可以引入一个带有单个方法的类型类,它给出了指针的名称,给定了约束:
class GimmeStaticPtr c where
gimmeStaticPtr :: StaticPtr (Dict c)
这里有一个轻微的技术诀窍。 StaticPtr
的类型索引类型为*
种类,而约束类型为Constraint
。因此,我们重用了constraints库中的一个技巧,该技巧包括将约束包装到数据类型(上面的Dict
)中,就像所有数据类型都是*
类型一样。具有关联的GimmeStaticPtr
实例的约束称为静态约束。
通常,组合静态约束以获得更多静态约束有时很有用。 StaticPtr
不可组合,但Closure
是。所以distributed-closure
实际上做的是定义一个类似的类,我们称之为
class GimmeClosure c where
gimmeClosure :: Closure (Dict c)
现在我们可以用与您类似的方式定义closurePure
:
closurePure :: (Typeable a, GimmeClosure (Binary a)) => a -> Closure a
如果将来,编译器可以通过根据需要生成静态指针来即时解决GimmeClosure
约束,那将是很好的。但就目前而言,最接近的是模板Haskell。 distributed-closure提供了一个模块,用于在类GimmeClosure (Cls a)
的定义站点自动生成Cls
约束。请参阅withStatic
here。
顺便提一下,Edsko de Vries给出了great talk关于分布式封闭及其中所包含的想法的内容。
答案 1 :(得分:2)
让我们花点时间考虑一下你的要求。回想一下,类型类基本上是字典传递的简写。所以让我们改写:
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
的参数无法关闭。如果允许{i}允许无限制地创建BinaryDict
,那么根据用户数据,这个功能是不可能的。我们需要:
closurePure :: (Typeable a) => Static (BinaryDict a) -> a -> Closure a
也就是说,我们需要静态指针表中所需Binary
个实例的条目。因此,您的枚举解决方案,以及为什么我怀疑需要这样的解决方案。我们也不能指望自动枚举它 ,因为有无数的实例。
reflection
?我听不到你的声音)。这可能至少在分布式Haskell论文中被忽略了(我还没有读过)。
我们可以通过简单地创建一个具体枚举每个类的每个实例的类来解决这个问题(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
是一个“语法形式”而不是一个函数 - 通过提及它,我们指出实际上必须生成Binary
Int
实例并且记录在静态指针表中。
如果您感觉厚颜无耻,可以启用{-# 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)
而不是那些傲慢的类型错误。