`Data.Proxy`的目的是什么?

时间:2014-03-01 15:35:19

标签: haskell types

来自Data.Proxy

Proxy似乎只是一个

data Proxy s

我什么时候需要这样一种无人居住的类型,或者更确切地说,它是什么使我无法做到的,与其他方法相比,它何时简化了事情,以及它在实践中如何使用?

1 个答案:

答案 0 :(得分:20)

我经常将Proxy与其Data.Tagged中的合作伙伴一起使用,如documentation所示,以避免不安全地传递虚假参数。

例如,

data Q

class Modulus a where
  value :: Tagged a Int

instance Modulus Q where
  value = Tagged 5

f x y = (x+y) `mod` (proxy value (Proxy::Proxy Q))

在没有Tagged的情况下编写该文件的另一种方法是

data Q

class Modulus a where
  value :: Proxy a -> Int

instance Modulus Q where
  value _ = 5

f x y = (x+y) `mod` (value (Proxy::Proxy Q))

在这两个示例中,我们还可以删除Proxy类型,只需使用undefined :: Q传递幻像类型。但是,使用undefined是generally frowned upon,因为如果要评估该值,可能会出现问题。请考虑以下事项:

data P = Three
       | Default

instance Modulus P where
  value Three = 3
  value _ = 5

f x y = (x+y) `mod` (value (undefined :: P))

如果我使用Default构造函数,这是编写实例的有效方法,程序会崩溃,因为我正在尝试评估undefined。因此Proxy类型为幻像类型提供了类型安全性。

修改

正如卡尔所指出的,Proxy的另一个好处是能够拥有除*以外的幻影类型。例如,我正在搞乱类型列表:

{-# LANGUAGE KindSignatures, DataKinds, TypeOperators, 
    MultiParamTypeClasses, PolyKinds, FlexibleContexts, 
    ScopedTypeVariables #-}

import Data.Tagged
import Data.Proxy

class Foo (a::[*]) b where
  foo:: Tagged a [b]

instance Foo '[] Int where
  foo = Tagged []

instance (Foo xs Int) => Foo (x ': xs) Int where
  foo = Tagged $ 1 : (proxy foo (Proxy :: Proxy xs)) -- xs has kind [*]

toUnary :: [Int]
toUnary = proxy foo (Proxy :: Proxy '[Int, Bool, String, Double, Float])

但是,由于undefined,因此其类型必须为*#。如果我在我的示例中尝试使用undefined,我需要undefined :: '[Int, Bool, String, Double, Float]之类的内容,这会导致编译错误:

Kind mis-match
    Expected kind `OpenKind',
    but '[Int, Bool, String, Double, Float] has kind `[*]'

有关种类的更多信息,请检查this。鉴于错误消息,我希望能够写undefined :: Int#,但我仍然得到错误Couldn't match kind # against *,所以显然这是一个GHC错误消息的错误,或者我的一个简单的错误