현대 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를 유지하면서 유형 검사를 돕기 위해 컴파일 타임에 실제로 존재해야합니다 (이 경우 프로그램은 컴파일하지 않고 컴파일되지만이 고안된 예는 더 일반적인 문제입니다) (컴파일시에만 필요한 증명 또는 프록시)-프록시에는 생성자가 하나 뿐이며 type 인수는 팬텀 유형입니다.

ghc를 사용하여 컴파일하면 -ddump-stg적어도 STG 단계에서 생성자에 대한 프록시 인수 나 생성자에 대한 세 번째 인수가 지워지지 않습니다.

이것을 컴파일 타임으로 표시하거나 ghc가 증거 삭제를 수행하고 제외하도록 돕는 방법이 있습니까?

답변:


20

실제로 코드는 Proxy생성자에 s가 저장되도록합니다.

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

일반적으로 바닥 때문에 엄격하지 않은 프록시는 지울 수 없습니다. Proxy그리고 undefined둘 다 유형 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같은 확장 : AllowAmbiguousTypesTypeApplications예를 들어, 당신이 직접 의미 유형을 적용 할 수 있다는 것을 의미합니다. 유스 케이스가 무엇인지 모르지만 다음과 같은 (고려 된) 예를 들어보십시오.

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.