现代GHC版本是否具有某种形式的证明擦除?


22

假设我有一个仅出于类型系统的利益而存在的参数,例如在此小程序中:

{-# LANGUAGE GADTs #-}
module Main where
import Data.Proxy
import Data.List

data MyPoly where
  MyConstr :: Proxy a -> a -> (Proxy a -> a -> Int -> Int) -> MyPoly

listOfPolys :: [MyPoly]
listOfPolys = [MyConstr Proxy 5 (const (+))
              , MyConstr Proxy 10 (const (+))
              , MyConstr Proxy 15 (const (+))]

main = print $ foldl' (\v (MyConstr p n a) -> a p n v) 0 listOfPolys

结构中的Proxy参数和成员只需要在编译时就存在,以在维护多态MyPoly的同时帮助类型检查(在这种情况下,程序将在没有它的情况下进行编译,但是这个人为设计的示例是一个更普遍的问题,证明或仅在编译时需要的代理)-代理只有一个构造函数,并且type参数是幻像类型。

使用ghc with进行编译-ddump-stg表明,至少在STG阶段,不会删除构造函数的Proxy参数或构造函数的第三个参数。

有什么方法可以将它们标记为仅编译时,或者以其他方式帮助ghc进行证明擦除并将其排除在外?

Answers:


20

确实,您的代码确实导致将Proxys存储在构造函数中:

ProxyOpt.listOfPolys8 :: ProxyOpt.MyPoly
[GblId, Caf=NoCafRefs, Unf=OtherCon []] =
    CCS_DONT_CARE ProxyOpt.MyConstr! [Data.Proxy.Proxy
                                      ProxyOpt.listOfPolys9
                                      ProxyOpt.listOfPolys4];

但是,只需进行很小的更改,就可以得到所需的优化。没有更多Proxy

ProxyOpt.listOfPolys8 :: ProxyOpt.MyPoly
[GblId, Caf=NoCafRefs, Unf=OtherCon []] =
    CCS_DONT_CARE ProxyOpt.MyConstr! [ProxyOpt.listOfPolys9
                                      ProxyOpt.listOfPolys4];

我做了什么 我做了Proxy严格

data MyPoly where
  MyConstr :: !(Proxy a) -> a -> (Proxy a -> a -> Int -> Int) -> MyPoly
           -- ^ --

通常,由于存在底部,我们无法删除非严格代理。Proxyundefined都是类型,Proxy a但它们在观察上并不等效,因此我们必须在运行时加以区分。

相反,严格Proxy仅具有一个值,因此GHC可以优化该值。

但是,没有类似的功能可以优化掉(非构造函数)函数参数。您的字段(Proxy a -> a -> Int -> Int)将需要Proxy在运行时。


15

有两种方法可以实现您想要的。

较旧的方法是使用GHC.Prim中的Proxy#,可以确保在编译时将其删除。

{-# LANGUAGE GADTs, MagicHash #-}
module Main where

import Data.List
import GHC.Prim

data MyPoly where
  MyConstr :: Proxy# a -> a -> (Proxy# a -> a -> Int -> Int) -> MyPoly

listOfPolys :: [MyPoly]
listOfPolys = [MyConstr proxy# 5 (\_ -> (+))
              , MyConstr proxy# 10 (\_ -> (+))
              , MyConstr proxy# 15 (\_ -> (+))]

虽然这有点麻烦。

另一种方法是Proxy完全放弃:

{-# LANGUAGE GADTs #-}

module Main where

import Data.List

data MyPoly where
  MyConstr :: a -> (a -> Int -> Int) -> MyPoly

listOfPolys :: [MyPoly]
listOfPolys = [ MyConstr 5  (+)
              , MyConstr 10 (+)
              , MyConstr 15 (+)
              ]

main = print $ foldl' (\v (MyConstr n a) -> a n v) 0 listOfPolys

如今,我们有一些工具可以简化以下工作Proxy:像AllowAmbiguousTypes和这样的扩展名TypeApplications,意味着您可以直接应用您想要的类型。我不知道您的用例是什么,但请看以下(做作的)示例:

import Data.Proxy

asTypeP :: a -> Proxy a -> a
asTypeP x _ = x

readShow :: (Read a, Show a) => Proxy a -> String -> String
readShow p x = show (read x `asTypeP` p)

>>> readShow (Proxy :: Proxy Int) "01"
"1"

我们想要读取然后显示某种类型的值,因此我们需要一种指示实际类型是什么的方法。使用扩展程序的方法如下:

{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables #-}

readShow :: forall a. (Read a, Show a) => String -> String
readShow x = show (read x :: a)

>>> readShow @Int "01"
"1"

我认为,最后一种选择(无代理)是最好的选择。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.