我正在研究真实世界的Haskell,作为练习的一部分,我意识到我想要一个类似于的函数show :: Show(a) => a -> String
,但该函数使Strings保持不变(而不是转义引号)。用伪代码,我想要的是:
show' :: String -> String
show' = id
show':: Show(a) => a -> String
show' = show
显然,这是行不通的(ghc抱怨多个类型签名和多个声明)。看来我应该改用typeclass。当我尝试编写此代码时,编译器会不断建议我添加更多语言扩展:
{-# 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
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句