cro*_*eea 16 reflection haskell
我希望有人可以对Data.Reflection中的黑魔法有所了解.相关片段是:
{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE KindSignatures #-}
module Data.Reflection
(
Reifies(..)
, reify
) where
import Data.Proxy
import Unsafe.Coerce
class Reifies s a | s -> a where
-- | Recover a value inside a 'reify' context, given a proxy for its
-- reified type.
reflect :: proxy s -> a
newtype Magic a r = Magic (forall (s :: *). Reifies s a => Proxy s -> r)
-- | Reify a value at the type level, to be recovered with 'reflect'.
reify :: forall a r. a -> (forall (s :: *). Reifies s a => Proxy s -> r) -> r
reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy
Run Code Online (Sandbox Code Playgroud)
reify
.也许我错过了一些关于评估顺序的简单方法,但看起来unsafeCoerce::a->b
它应用于三个参数.unsafeCoerce
?k
定义中实际评估的函数在哪里reify
?任何实例在哪里Reifes
?例如,我可以在GHCi中运行以下行,只加载Data.Reflection和Data.Proxy(并设置-XScopedTypeVariables):.
reify (3::Int) (\(_:: Proxy q) -> print $ reflect (Proxy :: Proxy q))
幻影神化的类型在哪里/什么?
newtype Magic
?sha*_*haf 22
在了解此实现之前,您应该了解API.最初的想法(反映了对类型级别的任意指针)在本文中进行了解释,该文章是在" 慢 "版本中实现的reflection
.所以我假设你已经知道它是如何工作的,以及如何使用API." fast "是同一API的另一个实现,它使用一些GHC特定的技巧来加快速度.所以:
unsafeCoerce :: a -> b
确实应用于三个参数,这意味着b
必须是两个参数函数的类型.特别是,这unsafeCoerce
类似于:Magic a r -> (Proxy s -> a) -> Proxy s -> r
.
嘿."同构".
更严重的是:理解GHC的类型类的实现很重要,这涉及字典传递.当你有类似的东西
class Num a where
plus :: a -> a -> a
negate :: a -> a
foo :: Num a => a -> a
foo x = plus x (negate x)
Run Code Online (Sandbox Code Playgroud)
它被翻译成类似的东西
data Num a = Num { plus :: a -> a -> a, negate :: a -> a }
foo :: Num a -> a -> a
foo dict x = plus dict x (negate dict x)
Run Code Online (Sandbox Code Playgroud)
GHC根据您使用的类型确定要传入的字典foo
.注意单参数函数如何变成双参数函数.
所以类型类的实现是传递额外的字典参数.但请注意,作为优化,我们可以使用newtype
而不是data
当类只有一个方法时.例如
class Default a where
def :: a
doubleDef :: Default a => (a, a)
doubleDef = (def, def)
Run Code Online (Sandbox Code Playgroud)
变成
newtype Default a = Default { def :: a }
doubleDef :: Default a -> (a, a)
doubleDef dict = (def dict, def dict)
Run Code Online (Sandbox Code Playgroud)
但这产生的def
是操作上的unsafeCoerce
.
Magic k
是 k
,只是一个不同的类型.所以函数unsafeCoerce (Magic k)
是函数k
,其类型被修改.它的功能完全相同.
所以让我们考虑一下这个类是如何编译的(我将转而Proxy
使用大写P
来简化事情).
class Reifies s a | s -> a where
reflect :: Proxy s -> a
foo :: Reifies s a => ...
Run Code Online (Sandbox Code Playgroud)
变成
newtype Reifies s a = Reifies { reflect :: Proxy s -> a }
foo :: Reifies s a -> ...
-- which is unsafeCoerce-compatible with
foo :: (Proxy s -> a) -> ...
Run Code Online (Sandbox Code Playgroud)
所以,在操作上,
newtype Magic a r = Magic (forall s. Reifies s a => Proxy s -> r)
Run Code Online (Sandbox Code Playgroud)
是unsafeCoerce
与...兼容的
newtype Magic a r = Magic (forall s. (Proxy s -> a) -> Proxy s -> r)
Run Code Online (Sandbox Code Playgroud)所以现在我们可以看到它是如何工作的:
reify
得到两个参数,一个值:: a
和一个函数:: forall s. Reifies s a => Proxy s -> r
.由于函数的形状与形状相同Magic
,我们将其转换为值:: Magic a r
.在操作上,Magic a r
大致相同forall s. (Proxy s -> a) -> Proxy s -> r
,所以我们可以交叉手指unsafeCoerce
.当然,(Proxy s -> a) -> Proxy s -> r
是同构的a -> r
,所以我们只需要传递正确的函数(const a
)和Proxy
值,我们就完成了.
魔术一直在你的功能中.