Haskell 根据返回类型选择typeclass

Haskell 根据返回类型选择typeclass,haskell,typeclass,Haskell,Typeclass,我希望能够有一个函数,它的实现将根据返回类型的手动类型规范选择一个typeclass 下面是一个人为的示例:一个typeclass和两个实例: class ToString a where toString :: a -> String instance ToString Double where toString = const "double" instance ToString Int where toString = const "int" 我可以通过使用Intt

我希望能够有一个函数,它的实现将根据返回类型的手动类型规范选择一个typeclass

下面是一个人为的示例:一个typeclass和两个实例:

class ToString a where
  toString :: a -> String

instance ToString Double where
  toString = const "double"

instance ToString Int where
  toString = const "int"
我可以通过使用
Int
type调用toString来选择实例:

function :: String
function = toString (undefined :: Int)
到目前为止还不错。我想做的是能够编写函数,因此它将适用于任何
a
,如果它存在类型类:

function' :: (ToString a) => String
function' = toString (undefined :: a)
这里,
函数“
不会编译,因为签名中的任何地方都没有提到
a
type参数,而且调用时也无法指定typeclass

因此,看起来唯一的选择是将
a
类型的类型信息传递到返回类型:

data Wrapper a = Wrapper {
  field :: String }

function'' :: (ToString a) => Wrapper a
function'' = Wrapper $ toString (undefined :: a)

showToString :: String
showToString = field (function'' :: Wrapper Int)
我定义了一个
包装器
类型,仅用于承载类型信息。在
showToString
中,我希望既然我指定了
Wrapper
的确切类型,那么类型检查器可以推断
函数“
中的
a
是和
Int
,并选择
ToString
类型类的
Int
实例

但现实与我的希望不符,这是来自编译器的信息

无法推断由于使用“ToString”而产生的(ToString a0)


有没有一种方法,如何说服编译器,他可以在
函数'
中选择正确的类型类,因为我通过类型声明
::Wrapper Int
来指定它?

首先,让我建议您使用,而不是使用自己的
Wrapper
类型,其目的正是这类东西

除此之外,还需要打开
-XScopedTypeVariables
扩展名,否则
a
类型变量只存在于类型签名本身中,而不存在于本地绑定的签名中

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Tagged

function''' :: forall a. ToString a => Tagged a String
function''' = Tagged $ toString (undefined :: a)
显式的
forall
对于
a
实际成为作用域变量是必要的,否则扩展就不会生效

然而。。。。 实际上,最好的办法可能是让class方法首先生成一个标记值:

class NamedType a where
  typeName :: Tagged a String

instance NamedType Double where
  typeName = Tagged "double"
instance NamedType Int where
  typeName = Tagged "int"
...
或者根本不编写自己的类,而是使用:


当然,这将给出实际的大写类型名称,并且可能适用于您实际上不希望使用的类型。

首先,让我建议您使用,而不是使用自己的
包装器
类型,它的用途正是这类内容

除此之外,还需要打开
-XScopedTypeVariables
扩展名,否则
a
类型变量只存在于类型签名本身中,而不存在于本地绑定的签名中

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Tagged

function''' :: forall a. ToString a => Tagged a String
function''' = Tagged $ toString (undefined :: a)
显式的
forall
对于
a
实际成为作用域变量是必要的,否则扩展就不会生效

然而。。。。 实际上,最好的办法可能是让class方法首先生成一个标记值:

class NamedType a where
  typeName :: Tagged a String

instance NamedType Double where
  typeName = Tagged "double"
instance NamedType Int where
  typeName = Tagged "int"
...
或者根本不编写自己的类,而是使用:


当然,这将为您提供实际的大写类型名称,并且可能适用于您实际上不希望使用的类型。

leftaroundabout的答案可能就是您想要的答案。但为了完整性,您还可以做一件事:

unwrap :: Wrapper a -> a
unwrap = error "can't actually unwrap"

function'' :: (ToString a) => Wrapper a
function'' = x
  where
    x = Wrapper (toString (unwrap x))
我希望
a
传递给
toString
,但我的类型中只显示
包装器a
。所以我只定义了一个函数,它接受
包装器a
并生成
包装器a
——这样的函数不能有一个真正的实现,但我们并没有使用它作为它的返回值——然后将它应用到
包装器a


还有一点额外的尴尬,因为
包装器a
出现在结果中而不是参数中,但是这个(有点愚蠢的)递归解决了这个问题。

leftaroundabout的答案可能就是您想要的答案。但为了完整性,您还可以做一件事:

unwrap :: Wrapper a -> a
unwrap = error "can't actually unwrap"

function'' :: (ToString a) => Wrapper a
function'' = x
  where
    x = Wrapper (toString (unwrap x))
我希望
a
传递给
toString
,但我的类型中只显示
包装器a
。所以我只定义了一个函数,它接受
包装器a
并生成
包装器a
——这样的函数不能有一个真正的实现,但我们并没有使用它作为它的返回值——然后将它应用到
包装器a


还有一点额外的尴尬,因为
包装器a
出现在结果中而不是参数中,但是这个(有点愚蠢的)递归解决了这个问题。

我猜您可能需要存在类型或类似的东西。我只想让您注意到,在
函数“”
中,类型表达式
未定义::a
可以编写
未定义::b
而不改变其含义,因为类型变量没有作用域(除非打开相应的GHC扩展)。我想您可能需要存在类型或类似的东西。我只想让您注意到,在
函数“”
中,类型表达式
undefined::a
可以写入
undefined::b
,而不会改变其含义,因为类型变量的作用域不受限制(除非打开相应的GHC扩展)。