3

我尝试编写一个变体show,将字符串与 的其他实例不同Show,不包括 " 并直接返回字符串。但我不知道该怎么做。模式匹配?警卫?我找不到任何关于在任何文档中。

这是我尝试过的,但无法编译:

show_ :: Show a => a -> String
show_ (x :: String) = x
show_ x             = show x
4

3 回答 3

5

如果可能,您应该按照@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无论如何。但这有点晦涩,所以我认为最好是明确的。

于 2014-10-12T18:06:11.043 回答
4

您可以将其包装并为其newtype制作自定义Show实例:

newtype PrettyString = PrettyString { toString :: String }

instance Show PrettyString where
  show (PrettyString s) = "$$" ++ s ++ "$$" -- for example

然后像下面这样使用它:

main = getLine >>= print . PrettyString
于 2014-10-12T18:02:09.127 回答
2

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_ 类函数,默认显示和逗号分隔的列表。

所以现在这个类具有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> 
于 2014-10-12T21:01:09.997 回答