Data.Typeable.cast 到存在类型
Data.Typeable.cast to an existential type
所以这是我的对象系统传奇的延续 (, )。
这部分基本上归结为以下内容。
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE KindSignatures #-}
import Data.Typeable
import GHC.Exts (Constraint)
data Obj (cls :: * -> Constraint) = forall o. (cls o, Typeable o) => Obj o
downcast :: Obj a -> Maybe (Obj b)
downcast (Obj x) = do
cx <- cast x
return $ Obj cx
downcast
的定义失败并出现此错误:
• Could not deduce: b o0 arising from a use of ‘Obj’
from the context: (a o, Typeable o)
bound by a pattern with constructor:
Obj :: forall (cls :: * -> Constraint) o.
(cls o, Typeable o) =>
o -> Obj cls,
in an equation for ‘downcast’
at downcast.hs:12:11-15
• In the second argument of ‘($)’, namely ‘Obj cx’
In a stmt of a 'do' block: return $ Obj cx
In the expression:
do cx <- cast x
return $ Obj cx
• Relevant bindings include
cx :: o0 (bound at moo.hs:13:22)
downcast :: Obj a -> Maybe (Obj b) (bound at downcast.hs:12:1)
我不太明白为什么会出现这个错误:( 可以修复吗?
当您的 Haskell 被翻译成 GHC Core 时,类(以及随之而来的逻辑编程结构,如蕴涵)已经无处可寻。它们被编译器转换为 字典传递 代码——每个 instance
成为一个记录(一个常规值)并且每个方法成为该记录的一个成员。 (更多细节见。)
所以一个打包约束的构造函数,
data Obj c where -- I'm using GADT syntax
Obj :: c a => a -> Obj c
确实在运行时间由常规产品类型表示,
data Obj c where
Obj :: c a -> a -> Obj c
其中 c a
字段是表示 c a
实例的 运行time 方法字典。
在 运行 时将 Obj c
向下转换为 Obj c'
,即使您有办法测试具体的 a
是两者的实例 c
和 c'
,你仍然需要以某种方式为 c'
合成一个字典。由于 c'
通常会包含比 c
更多的方法,这相当于要求计算机为您编写程序。
, I think your best bet would be to build knowledge about your specific class hierarchy into your runtime system under a closed-world assumption. If you have an oracle which can look up an instance's actual runtime dictionary,
oracle :: MonadRuntime m => TypeRep a -> TypeRep c -> m (Maybe (Dict (c a)))
然后你可以写cast
(有一些不舒服的table类型争论):
data Obj c where
Obj :: c a => TypeRep a -> a -> Obj c
cast :: forall c c' m. (MonadRuntime m, Typeable c') => Obj c -> m (Maybe (Obj c'))
cast (Obj tr x) = do
mdict <- oracle tr (typeRep @c')
case mdict of
Just Dict -> return (Just (Obj tr x))
Nothing -> return Nothing
请注意,此 cast
实际上允许您(尝试)将对象的接口更改为 任何 其他接口,而不仅仅是那些从对象的静态类型派生的接口. (在 C# 中,您可以通过向上转换为 object
然后向下转换来实现。)您可以通过在 cast
的上下文中要求一个 entailment 来防止这种情况:
cast :: forall c c' m. (MonadRuntime m, Typeable c', Class c c') => Obj c -> m (Maybe (Obj c'))
(当然,在 运行 时实际上不会使用该蕴含。)
挑战在于实施oracle
!我认为这将是 不好玩 的挑战之一,所以我会给你一两个提示。
你的 Runtime
monad 可能是某种 Reader
带有查找 table 映射(TypeRep
的 a
和 c
s 到字典。 a
s 和 c
s 将需要存在量化,以便将它们存储在异构列表中。
data TableEntry where
TableEntry :: c a => TypeRep c -> TypeRep a -> TableEntry
type MonadRuntime = MonadReader [TableEntry]
然后oracle
需要查找匹配class/type对的TableEntry
,然后打开existential,拆开typeRep
建立类型相等和 return Just Dict
。 (特别是这部分听起来像是编写代码的噩梦。)
在你 运行 你的 MonadRuntime
程序之前,你需要构建 Table
包含你的程序关心的所有实例。
table = [
TableEntry (typeRep @Ord) (typeRep @Int),
TableEntry (typeRep @Eq) (typeRep @Bool)
]
总而言之,我不明白这有什么值得头疼的。类型 类 从根本上不同于 OO 类(它们甚至不像 OO 接口),因此很难使用它们来建模 OO 类 就不足为奇了。
所以这是我的对象系统传奇的延续 (
这部分基本上归结为以下内容。
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE KindSignatures #-}
import Data.Typeable
import GHC.Exts (Constraint)
data Obj (cls :: * -> Constraint) = forall o. (cls o, Typeable o) => Obj o
downcast :: Obj a -> Maybe (Obj b)
downcast (Obj x) = do
cx <- cast x
return $ Obj cx
downcast
的定义失败并出现此错误:
• Could not deduce: b o0 arising from a use of ‘Obj’
from the context: (a o, Typeable o)
bound by a pattern with constructor:
Obj :: forall (cls :: * -> Constraint) o.
(cls o, Typeable o) =>
o -> Obj cls,
in an equation for ‘downcast’
at downcast.hs:12:11-15
• In the second argument of ‘($)’, namely ‘Obj cx’
In a stmt of a 'do' block: return $ Obj cx
In the expression:
do cx <- cast x
return $ Obj cx
• Relevant bindings include
cx :: o0 (bound at moo.hs:13:22)
downcast :: Obj a -> Maybe (Obj b) (bound at downcast.hs:12:1)
我不太明白为什么会出现这个错误:( 可以修复吗?
当您的 Haskell 被翻译成 GHC Core 时,类(以及随之而来的逻辑编程结构,如蕴涵)已经无处可寻。它们被编译器转换为 字典传递 代码——每个 instance
成为一个记录(一个常规值)并且每个方法成为该记录的一个成员。 (更多细节见
所以一个打包约束的构造函数,
data Obj c where -- I'm using GADT syntax
Obj :: c a => a -> Obj c
确实在运行时间由常规产品类型表示,
data Obj c where
Obj :: c a -> a -> Obj c
其中 c a
字段是表示 c a
实例的 运行time 方法字典。
在 运行 时将 Obj c
向下转换为 Obj c'
,即使您有办法测试具体的 a
是两者的实例 c
和 c'
,你仍然需要以某种方式为 c'
合成一个字典。由于 c'
通常会包含比 c
更多的方法,这相当于要求计算机为您编写程序。
oracle :: MonadRuntime m => TypeRep a -> TypeRep c -> m (Maybe (Dict (c a)))
然后你可以写cast
(有一些不舒服的table类型争论):
data Obj c where
Obj :: c a => TypeRep a -> a -> Obj c
cast :: forall c c' m. (MonadRuntime m, Typeable c') => Obj c -> m (Maybe (Obj c'))
cast (Obj tr x) = do
mdict <- oracle tr (typeRep @c')
case mdict of
Just Dict -> return (Just (Obj tr x))
Nothing -> return Nothing
请注意,此 cast
实际上允许您(尝试)将对象的接口更改为 任何 其他接口,而不仅仅是那些从对象的静态类型派生的接口. (在 C# 中,您可以通过向上转换为 object
然后向下转换来实现。)您可以通过在 cast
的上下文中要求一个 entailment 来防止这种情况:
cast :: forall c c' m. (MonadRuntime m, Typeable c', Class c c') => Obj c -> m (Maybe (Obj c'))
(当然,在 运行 时实际上不会使用该蕴含。)
挑战在于实施oracle
!我认为这将是 不好玩 的挑战之一,所以我会给你一两个提示。
你的 Runtime
monad 可能是某种 Reader
带有查找 table 映射(TypeRep
的 a
和 c
s 到字典。 a
s 和 c
s 将需要存在量化,以便将它们存储在异构列表中。
data TableEntry where
TableEntry :: c a => TypeRep c -> TypeRep a -> TableEntry
type MonadRuntime = MonadReader [TableEntry]
然后oracle
需要查找匹配class/type对的TableEntry
,然后打开existential,拆开typeRep
建立类型相等和 return Just Dict
。 (特别是这部分听起来像是编写代码的噩梦。)
在你 运行 你的 MonadRuntime
程序之前,你需要构建 Table
包含你的程序关心的所有实例。
table = [
TableEntry (typeRep @Ord) (typeRep @Int),
TableEntry (typeRep @Eq) (typeRep @Bool)
]
总而言之,我不明白这有什么值得头疼的。类型 类 从根本上不同于 OO 类(它们甚至不像 OO 接口),因此很难使用它们来建模 OO 类 就不足为奇了。