Data.Proxy
的 Proxy
似乎只是一个
data Proxy s
我什么时候需要这样一种无人居住的类型,或者更确切地说,它是什么使我无法做到的,与其他方法相比,它何时简化了事情,以及它在实践中如何使用?
答案 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错误消息的错误,或者我的一个简单的错误