Các phiên bản GHC hiện đại có loại tẩy bằng chứng nào không?


22

Giả sử tôi có một tham số chỉ tồn tại vì lợi ích của hệ thống loại, ví dụ như trong chương trình nhỏ này:

{-# 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

Các đối số Proxy và các thành viên trong cấu trúc chỉ thực sự cần tồn tại vào thời gian biên dịch để giúp kiểm tra kiểu trong khi duy trì MyPoly đa hình (trong trường hợp này, chương trình sẽ biên dịch mà không có nó, nhưng ví dụ giả định này là một vấn đề chung hơn khi có bằng chứng hoặc proxy chỉ cần thiết trong thời gian biên dịch) - chỉ có một hàm tạo cho Proxy và đối số loại là loại ảo.

Biên dịch với ghc với các -ddump-stgchương trình ít nhất là ở giai đoạn STG, không có sự xóa bỏ đối số Proxy đối với hàm tạo hoặc đối số thứ ba đối với hàm tạo.

Có cách nào để đánh dấu những thứ này chỉ là thời gian biên dịch, hay nói cách khác là giúp ghc thực hiện việc xóa bằng chứng và loại trừ chúng?

Câu trả lời:


20

Thật vậy, mã của bạn không dẫn đến Proxys được lưu trữ trong hàm tạo:

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

Tuy nhiên, với một thay đổi nhỏ, chúng tôi có được sự tối ưu hóa mong muốn. Không còn nữa Proxy!

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

Tôi đã làm gì? Tôi đã làm cho Proxylĩnh vực nghiêm ngặt :

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

Nói chung, chúng tôi không thể xóa các proxy không nghiêm ngặt vì đáy. Proxyundefinedcả hai loại Proxy anhưng chúng không tương đương quan sát, vì vậy chúng ta phải phân biệt chúng trong thời gian chạy.

Thay vào đó, một nghiêm ngặt Proxychỉ có một giá trị, vì vậy GHC có thể tối ưu hóa điều đó.

Mặc dù vậy, không có tính năng tương tự để tối ưu hóa một tham số hàm (không phải hàm tạo). Lĩnh vực của bạn (Proxy a -> a -> Int -> Int)sẽ yêu cầu một Proxythời gian chạy.


15

Có hai cách để thực hiện những gì bạn muốn.

Cách cũ hơn một chút là sử dụng Proxy # từ GHC.Prim, được đảm bảo sẽ bị xóa tại thời gian biên dịch.

{-# 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 (\_ -> (+))]

Mặc dù điều này là một chút rườm rà.

Một cách khác là từ bỏ Proxyhoàn toàn:

{-# 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

Ngày nay, chúng tôi có một số công cụ giúp làm việc dễ dàng hơn mà không cần Proxy: tiện ích mở rộng như AllowAmbiguousTypesTypeApplications, chẳng hạn, có nghĩa là bạn có thể áp dụng loại bạn muốn nói trực tiếp. Tôi không biết trường hợp sử dụng của bạn là gì, nhưng hãy lấy ví dụ (giả định) này:

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"

Chúng tôi muốn đọc và sau đó hiển thị một giá trị của một số loại, vì vậy chúng tôi cần một cách để chỉ ra loại thực tế là gì. Đây là cách bạn làm điều đó với các tiện ích mở rộng:

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

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

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

Theo tôi, sự thay thế cuối cùng (không có proxy) là cách tốt nhất.
chi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.