有没有一种方法可以直接引用Haskell中的typeclass实例?

时间:2019-02-28 21:45:57

标签: haskell

这样做的好处是可以将有关类型的某些元数据存储在规范位置。有时,在类型上使用某些实例方法之前,先获取类型的值并不方便;例如,如果我有:

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中的一流实体吗?

2 个答案:

答案 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扩展名。

新方法是使用TypeApplicationsAllowAmbiguousTypes扩展名:

{-# LANGUAGE TypeApplications, AllowAmbiguousTypes #-}

class Foo a where
    foo :: String

instance Foo FancyPants where
    foo = "a fancypants instance"

fooString :: String
fooString = foo @FancyPants

哪个看起来更好,但是在实践中使用它会更令人恼火,原因是我无法完全动手。

回答这个问题了吗?