我尝试编写一个变体show
,将字符串与 的其他实例不同Show
,不包括 " 并直接返回字符串。但我不知道该怎么做。模式匹配?警卫?我找不到任何关于在任何文档中。
这是我尝试过的,但无法编译:
show_ :: Show a => a -> String
show_ (x :: String) = x
show_ x = show x
我尝试编写一个变体show
,将字符串与 的其他实例不同Show
,不包括 " 并直接返回字符串。但我不知道该怎么做。模式匹配?警卫?我找不到任何关于在任何文档中。
这是我尝试过的,但无法编译:
show_ :: Show a => a -> String
show_ (x :: String) = x
show_ x = show x
如果可能,您应该按照@wowofbob 的建议将您的类型值包装String
在 a中。newtype
但是,有时这是不可行的,在这种情况下,有两种通用方法可以使某物String
专门识别。
第一种方法是自然的 Haskell 方法,是使用类型类,就像Show
为不同类型获得不同的行为一样。所以你可以写
class Show_ a where
show_ :: a -> String
接着
instance Show_ String where
show_ x = x
instance Show_ Int where
show_ x = show x
对于您要使用的任何其他类型,依此类推。这样做的缺点是您需要为您想要的所有类型显式写出Show_
实例。
@AndrewC 展示了如何将每个实例缩减为一行,但您仍然必须明确列出它们。理论上,您可以解决此问题,如this question中所述,但这并不令人愉快。
第二种选择是使用Typeable
类获取真正的运行时类型信息,在这种特殊情况下非常简短:
import Data.Typeable
[...]
show_ :: (Typeable a, Show a) => a -> String
show_ x =
case cast x :: Maybe String of
Just s -> s
Nothing -> show x
这不是一种自然的 Haskell 式方法,因为这意味着调用者无法从类型中得知函数将做什么。
类型类通常提供受约束的多态性,特定函数的行为的唯一变化必须来自相关类型类实例的变化。该类Show_
从其名称中给出了一些指示,并且可能会记录在案。
然而Typeable
是一个非常普通的类。您将所有内容委托给您正在调用的特定功能;Typeable
对于许多不同的具体类型,具有约束的函数可能具有完全不同的实现。
Typeable
最后,对更接近原始代码的解决方案的进一步阐述是使用几个扩展:
{-# LANGUAGE ViewPatterns, ScopedTypeVariables #-}
import Data.Typeable
[...]
show_ :: (Typeable a, Show a) => a -> String
show_ (cast -> Just (s :: String)) = s
show_ x = show x
的使用ViewPatterns
允许我们编写cast
模式的内部,这可能更适合更复杂的示例。事实上,我们可以省略:: String
类型约束,因为这种情况的主体强制s
成为 的结果类型show_
,即String
无论如何。但这有点晦涩,所以我认为最好是明确的。
您可以将其包装并为其newtype
制作自定义Show
实例:
newtype PrettyString = PrettyString { toString :: String }
instance Show PrettyString where
show (PrettyString s) = "$$" ++ s ++ "$$" -- for example
然后像下面这样使用它:
main = getLine >>= print . PrettyString
TL;博士:
复制前奏的方式并showList_
用作类函数来生成列表的实例,以便您可以覆盖 String 的定义。
警告
记录在案,wowofbob使用新类型包装器的答案是我在现实生活中会使用的简单、干净的解决方案,但我觉得看看 Prelude 如何做到这一点也很有启发性。
默认插入逗号
在前奏中这样做的方式是使Show
该类具有显示列表的功能,并具有可以覆盖的默认定义。
import Data.List (intercalate)
我会intercalate :: [a] -> [[a]] -> [a]
在内容之间使用逗号:
ghci> intercalate "_._" ["intercalate","works","like","this"]
"intercalate_._works_._like_._this"
所以现在这个类具有showList
函数的默认实现,重要的是,一个show_
只使用普通show
函数的默认实现。为了能够使用它,我们必须坚持该类型已经在Show
类型类中,但据我了解,这没关系。
class Show a => Show_ a where
show_ :: a -> String
showList_ :: [a] -> String
show_ = show
showList_ xs = '[' : intercalate ", " (map show_ xs) ++ "]"
真正的 Show 类出于效率原因使用类型函数String -> String
而不是String
直接使用函数,并使用优先参数来控制括号的使用,但为简单起见,我将跳过所有这些。
现在我们可以使用该showList
函数为列表提供一个实例:
instance Show_ a => Show_ [a] where
show_ xs = showList_ xs
Show a =>
类使实例变得超级简单现在我们来看一些例子。由于我们的默认show_
实现,我们不需要进行任何实际编程,除非我们想要覆盖默认值,我们将为 Char 做这件事,因为String ~ [Char]
.
instance Show_ Int
instance Show_ Integer
instance Show_ Double
instance Show_ Char where
show_ c = [c] -- so show_ 'd' = "d". You can put show_ = show if you want "'d'"
showList_ = id -- just return the string
现在"
在 ghci 中隐藏你的输出并没有多大用处,因为默认show
函数用于此,但如果我们使用putStrLn
,引号就会消失:
put :: Show_ a => a -> IO ()
put = putStrLn . show_
ghci> show "hello"
"\"hello\""
ghci> show_ "hello"
"hello"
ghci> put "hello"
hello
ghci> put [2,3,4]
[2, 3, 4]
ghci>