这样做的好处是可以将有关类型的某些元数据存储在规范位置。有时,在类型上使用某些实例方法之前,先获取类型的值并不方便;例如,如果我有:
class Foo a where
foo :: String
foo = "Foo"
这实际上不是有效的Haskell。相反,看来我必须有类似的东西:
class Foo a where
foo :: a -> String
foo = const "Foo"
,现在foo
实际上将具有类型Foo a => a -> String
,但是我实际上希望能够执行类似的操作,例如拥有类型foo
的{{1}}。为了使此方法在某些情况下有用,可能还需要遍历所有(范围内)实例,或者在其他情况下,以便能够具体实现给定类型的实例。
我想问题是实例和类型类不是Haskell中的一流实体吗?
答案 0 :(得分:8)
“老派”做法是提供一个“虚拟”参数,其目的只是帮助编译器找到合适的实例。在这个世界上,您的课程看起来像这样:
data Dummy a = Dummy
class Foo a where
foo :: Dummy a -> String
-- usage:
boolFoo = foo (Dummy :: Dummy Bool)
实际上,这种技巧无处不在,以至于Dummy
类型被半标准化为Data.Proxy
。
但是在现代GHC中,有一种更好的方法:TypeApplications
。
启用此扩展名后,您可以在调用类方法时直接指定类型:
class Foo a where
foo :: String
boolFoo = foo @Bool
(这不仅适用于类方法;它可与任何通用函数一起使用,但要注意类型参数的顺序!)
您可能还需要启用AllowAmbiguousTypes
才能声明此类。尽管我不确定我是否正确记住了这一点,并且我没有方便检查的计算机。
答案 1 :(得分:5)
(我仍然更喜欢)旧方法是使用 proxy 。
import Data.Proxy
class Foo a where
foo :: Proxy a -> String
instance Foo FancyPants where
foo _ = "a fancypants instance"
fooString :: String
fooString = foo (Proxy :: Proxy FancyPants)
因此我们实际上并不需要使用FancyPants
类型的值foo
,我们只需要一个Proxy FancyPants
的值-但您可以创建任何类型的代理想。这也可以在多态环境中完成。通常需要使用ScopedTypeVariables
扩展名。
新方法是使用TypeApplications
和AllowAmbiguousTypes
扩展名:
{-# LANGUAGE TypeApplications, AllowAmbiguousTypes #-}
class Foo a where
foo :: String
instance Foo FancyPants where
foo = "a fancypants instance"
fooString :: String
fooString = foo @FancyPants
哪个看起来更好,但是在实践中使用它会更令人恼火,原因是我无法完全动手。
回答这个问题了吗?