Haskell反思中的黑魔法

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)
  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

sha*_*haf 22

在了解此实现之前,您应该了解API.最初的想法(反映了对类型级别的任意指针)在本文中进行了解释,该文章是在" "版本中实现的reflection.所以我假设你已经知道它是如何工作的,以及如何使用API​​." fast "是同一API的另一个实现,它使用一些GHC特定的技巧来加快速度.所以:

  1. unsafeCoerce :: a -> b确实应用于三个参数,这意味着b必须是两个参数函数的类型.特别是,这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)
    
    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.

  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 => ...
    
    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)
  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. 魔术一直在你的功能中.

  • 在ghci 7.10中进行测试,如果你愿意变得棘手的话,可以说服GHC在没有newtype的情况下使用`impredicativeTypes'来执行unsafeCoerce:`让reify :: forall a r.a - >(forall s.Reifies sa => Proxy s - > r) - > r; reify ak =(unsafeCoerce unsafeCoerce ::(forall s.Reifies sa => Proxy s - > r) - >(Proxy s - > a) - > Proxy s - > r)k(const a)Proxy (4认同)
  • 参见4的结尾.我们`unsafeCoerce`是一个带有一个约束的函数和一个带有两个参数的函数的一个参数. (3认同)
  • 关于这个实现,有一点我不明白 - 但它似乎很基础,所以我现在质疑我是否真的理解它的任何一个!`magic ar`是`unsafeCoerce`-与`forall s兼容.(代理s - > a) - >代理s - > r`(因为我们知道`Reifies`的表示),但不是函数`k :: forall s.将sa =>代理s - > r`作为参数传递给`reflect` _also_`unsafeCoerce`-与该类型兼容?为什么不只是`unsafeCoerce k`并完全免除'魔术'? (2认同)
  • 从理论上讲,是的,但GHC对类型类和多态/ rank-n/impredicative类型的处理可能很滑.试图在类型`使用`unsafeCoerce k`(forall s.Reifies sa => Proxy s - > r) - >(代理s - > a) - >代理s - > r`,GHC试图选择一些特定的` s`使用.从理论上讲,你可以将整个多态函数传递给`unsafeCoerce`,但是这会以多态类型实例化参数,GHC不能很好地支持impredicativity.newtype让你解决这个问题. (2认同)