17

我希望有人能对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
  1. 我无法解析reify. 也许我错过了一些关于评估顺序的简单信息,但它看起来unsafeCoerce::a->b适用于三​​个参数。
  2. 中使用的同构类型是unsafeCoerce什么?
  3. k在 的定义中实际评估的函数在哪里reify
  4. 哪里有任何实例Reifes?例如,我可以在 GHCi 中运行以下行,仅加载 Data.Reflection 和 Data.Proxy(并设置 -XScopedTypeVariables):。

    reify (3::Int) (\(_:: Proxy q) -> print $ reflect (Proxy :: Proxy q))

  5. 幻影实体化类型在哪里/什么?

  6. 什么是“魔法” newtype Magic
4

2 回答 2

23

在了解此实现之前,您应该了解 API。本文解释了最初的想法(反映指向类型级别的任意指针),它在“”版本的reflection. 所以我假设你已经知道它是如何工作的,以及如何使用 API。“快速”是相同 API 的另一种实现,它使用某种特定于 GHC 的技巧来加快速度。所以:

  1. unsafeCoerce :: a -> b确实适用于三个参数,这意味着它b必须是两个参数函数的类型。特别是, this 的类型unsafeCoerce类似于:Magic a r -> (Proxy s -> a) -> Proxy s -> r.

  2. 呵呵。“同构”。

    更严肃一点:理解 GHC 的类型类实现很重要,这涉及到字典传递。当你有类似的东西

    class Num a where
      plus :: a -> a -> a
      negate :: a -> a
    foo :: Num a => a -> a
    foo x = plus x (negate x)
    

    它被翻译成类似的东西

    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)
    

    使用 GHC 在您使用foo. 请注意单参数函数如何变成双参数函数。

    所以类型类的实现就是传递一个额外的字典参数。但请注意,作为一种优化,我们可以在类只有一个方法时使用newtype代替。data例如

    class Default a where
      def :: a
    doubleDef :: Default a => (a, a)
    doubleDef = (def, def)
    

    变成

    newtype Default a = Default { def :: a }
    doubleDef :: Default a -> (a, a)
    doubleDef dict = (def dict, def dict)
    

    但这产生def的是可操作的unsafeCoerce

  3. Magic k k,只是类型不同。所以函数unsafeCoerce (Magic k)就是函数k,修改了它的类型。都是一样的功能。

  4. 所以让我们考虑一下这个类是如何编译的(我将切换到Proxy大写P以简化事情)。

    class Reifies s a | s -> a where
      reflect :: Proxy s -> a
    foo :: Reifies s a => ...
    

    变成

    newtype Reifies s a = Reifies { reflect :: Proxy s -> a }
    foo :: Reifies s a -> ...
    -- which is unsafeCoerce-compatible with
    foo :: (Proxy s -> a) -> ...
    

    所以,在操作上,

    newtype Magic a r = Magic (forall s. Reifies s a => Proxy s -> r)
    

    unsafeCoerce- 兼容

    newtype Magic a r = Magic (forall s. (Proxy s -> a) -> Proxy s -> r)
    
  5. 所以现在我们可以看到它是如何工作的:

    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值,我们就完成了。

  6. 魔法一直在你的功能中。

于 2013-07-22T18:34:56.867 回答
1

shachaf的回答不仅比我的好,而且是正确的,但是我将我的想法留在这里以供后代使用。


这远远高于我,但无论如何这是我的答案。

  1. Magic是 2 个参数的构造函数,并且unsafeCoerce是强制Magic k的,它的类型r -> Magic a r是 的类型const a。它在这里所做的是将函数从一种类型强制转换为另一种类型。所以unsafeCoerce我怀疑的第三个“参数”实际上是传递给结果的参数unsafeCoerce (Magic k :: Magic a r) (const a)
  2. 看起来大多数黑魔法都在于没有Reifies定义的实例,并且构造函数使用存在类型。存在类型实际被占用的方式似乎是通过unsafeCoerce.
  3. 它在构造函数的强制中定义为与 type 相同const a。这一点我是最了解的。
  4. 据我所知,这些实例不存在 - 存在类型似乎被占用,因为unsafeCoerce它使类型系统相信,无论是否存在任何实例,Magic a r都已经创建了一个类型的值。无论这种类型是否存在,它似乎都是“伪造”的,就像在伪造中一样,而不是在金属加工中。
  5. 我认为,第一个参数Magic是作为 a 的具体类型。Proxy
  6. newtype 的魔力在于Magic这一点:(forall (s :: *). Reifies s a => Proxy s -> r)这就是事物的类型Magic。它指定 , 的参数Proxys的存在类型Reifies,或者可以这么说,类型系统知道的唯一事实是它是 的实例Reifies。所以它知道无论s是什么,它都必须是一个实例,Reifies并且它可以使用那个实例,但这就是它所能做的一切。就像类型系统已经清除了关于该类型是什么的所有其他知识,并且只知道对于Proxy所持有的值,它可以使用其reflect上的函数——仅此而已。但是reflect恢复了值和类型,因为类型被编码Magic并且reflect似乎只是一个unsafeCoerce的功能const a。或者至少,这就是我收集到的。

这可能是完全错误的,但这似乎是碎片粘合在一起的方式。或者至少,这对我来说是多么有意义。

于 2013-07-22T17:43:16.340 回答