如何为单一类型专门化(重载)函数的行为?

我正在使用 Real World Haskell,并且作为练习的一部分,我意识到我想要一个类似于 的函数show :: Show(a) => a -> String,但它不影响字符串(而不是转义引号)。在伪代码中,我想要的是:

show' :: String -> String
show' = id

show':: Show(a) => a -> String
show' = show

显然这不起作用(ghc 抱怨多个类型签名和多个声明)。看来我应该改用类型类。当我尝试编写此代码时,编译器不断建议我添加更多语言扩展:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, IncoherentInstances #-}

class Show' a where
  show' :: a -> String

instance Show' String where
  show' = id

instance (Show a) => Show' a where
  show' = show

-- x == "abc"
x = show' "abc"
y = show' 9

起初,这似乎工作:x == "abc"y == "9"预期。但是当我尝试在另一个多态函数中使用它时,编译器似乎总是将其解析为一般实现:

use :: (Show a) => a -> String
use x = show' x

-- z == ""abc""
z = use "abc"

所以我在这里做错了,我想知道是否有办法在没有一堆语言扩展的情况下做到这一点(其中一些似乎不是我应该鲁莽使用的东西)。如何show'使用现有show函数定义它?

回答

来自 OP 的评论:

[我] 很惊讶,这么简单的功能描述起来这么难实现。

这根本不是简单的函数,因为它在某种程度上打破了基于类型类的多态性的通常保证。当我们看到一个实例

instance Show' a => Show' [a] where ...

我们通常假设此实例适用于所有列表类型,没有例外。对类型的普遍量化a真的意味着forall a.

重叠/不连贯的实例打破了这个假设,并允许像你这样的“特殊情况”,正如大卫在他的回答中指出的那样。这是一种可能的解决方案,但请注意,通过打破上述假设,重叠/不连贯的实例会使实例解析变得相当脆弱,有时会意外地导致错误的实例被采用。

作为替代方案,您可以考虑利用Typeable我认为争议较小的类型类:

import Data.Typeable

show' :: (Show a, Typeable a) => a -> String
show' x = case cast x of
   Just s  -> s           -- cast succeeded, it is a string
   Nothing -> show x      -- case failed, it is not a string

请注意Typeable,在类型中, 的存在表明该函数是使用临时多态定义的,允许我们检查是否a是这样和这样的类型,这在参数多态下通常是不可能的。

请注意,这并不需要IncoherentInstances也没有OverlappingInstances


以上是如何为单一类型专门化(重载)函数的行为?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>