最新の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

構造のプロキシ引数とメンバーは、多形性MyPolyを維持しながら型チェックを支援するためにコンパイル時に存在する必要があるだけです(この場合、プログラムはそれなしでコンパイルされますが、この不自然な例は、より一般的な問題です)コンパイル時にのみ必要な証明またはプロキシ)-Proxyのコンストラクタは1つだけで、type引数はファントムタイプです。

ghcを使用し-ddump-stgてコンパイルすると、少なくともSTGの段階では、コンストラクターへのProxy引数またはコンストラクターへの3番目の引数は消去されません。

これらをコンパイル時のみとしてマークする方法、またはghcが証拠消去を実行してそれらを除外するのを助ける方法はありますか?

回答:


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が、観測上同等ではないため、実行時に区別する必要があります。

代わりに、strictにProxyは1つの値しかないため、GHCはそれを最適化できます。

ただし、(コンストラクターではない)関数パラメーターを最適化する同様の機能はありません。実行時にフィールド(Proxy a -> a -> Int -> Int)が必要にProxyなります。


15

目的を達成するには2つの方法があります。

少し古い方法は、コンパイル時に消去されることが保証されている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 (\_ -> (+))]

これは少し面倒ですが。

もう1つの方法は、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"

私の意見では、最後の代替案(プロキシなし)が最良のものです。
カイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.