根据返回类型选择一个类型类

我希望能够有一个函数,该实现将基于它的返回类型的手动类型规范来选择一个类型类型。

这是一个人为的例子:一个typeclass和两个实例:

class ToString a where
  toString :: a -> String

instance ToString Double where
  toString = const "double"

instance ToString Int where
  toString = const "int"

我可以通过用Int类型调用toString来选择实例:

function :: String
function = toString (undefined :: Int)

到现在为止还挺好。 我想要做的是能够编写函数,所以如果存在类型类型a话,它将适用于任何类型的函数:

function' :: (ToString a) => String
function' = toString (undefined :: a)

在这里, function'不能编译,因为a类型参数未签名的任何地方提到的,它是不可能在调用指定类型类。

所以看起来唯一的选择是将有关a类型的类型信息传递给返回类型:

data Wrapper a = Wrapper {
  field :: String }

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

showToString :: String
showToString = field (function'' :: Wrapper Int)

我只是为了携带类型信息而定义一个Wrapper类型。 在showToString ,我的希望是,由于我指定了Wrapper的确切类型,因此类型检查Wrapper可以推断function''中的a是和Int并选择ToString类型类型的Int实例。

但现实并不符合我的希望,这是编译器的信息

无法推断(ToString a0)由于使用`toString'

有没有办法,如何说服编译器,他可以在function''选择正确的类型类型,因为我通过使用:: Wrapper Int类型声明来指定它?


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

除此之外,你需要打开-XScopedTypeVariables延长,否则a类型变量只存在于类型签名本身,而不是在本地绑定的签名。

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Tagged

function''' :: forall a. ToString a => Tagged a String
function''' = Tagged $ toString (undefined :: a)

显式forall对于a实际成为一个范围变量是必需的,否则这个扩展不会被引入。

然而....

实际上,最好的事情可能是让类方法首先产生一个标记值:

class NamedType a where
  typeName :: Tagged a String

instance NamedType Double where
  typeName = Tagged "double"
instance NamedType Int where
  typeName = Tagged "int"
...

或者根本不要编写自己的类,而是使用可键入的类:

import Data.Typeable

typeName' :: Typeable a => Tagged a String
typeName' = fmap show $ unproxy typeRep

当然,这会给你实际的大写字母类型名称,它可能适用于你实际上不希望它的类型。


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但只有Wrapper a显示在我的类型。 所以我只是定义了一个函数,它使用Wrapper a并生成a - 这样的函数不能有真正的实现,但是我们并没有将它用作返回值 - 并将它应用于Wrapper a

还有一点额外的尴尬,因为Wrapper a显示在结果中而不是参数,但是这种(稍微愚蠢的)递归会照顾到这一点。

链接地址: http://www.djcxy.com/p/43535.html

上一篇: Choose a typeclass based on return type

下一篇: Making a typeclass, cannot deduce from context