Haskell:“show”的变体,不将 String 和 Char 用引号括起来
我想要一个 show
的变体(我们称之为 label
),它的作用就像 show
一样,只是它不包装
或 " "
中的 >String' '
中的 Char
。示例:
> label 5
"5"
> label "hello"
"hello"
> label 'c'
"c"
我尝试手动实现此操作,但遇到了一些困难。这是我尝试过的:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Label where
class (Show a) => Label a where
label :: a -> String
instance Label [Char] where
label str = str
instance Label Char where
label c = [c]
-- Default case
instance Show a => Label a where
label x = show x
但是,由于默认情况的类与 instance Label [Char]
和 instance Label Char
重叠,因此这些类型不适用于 label
函数。
有没有提供这个功能的库函数?如果没有,是否有解决方法可以使上述代码正常工作?
I'd like a variant of show
(let's call it label
) that acts just like show
, except that it doesn't wrap String
s in " "
or Char
s in ' '
. Examples:
> label 5
"5"
> label "hello"
"hello"
> label 'c'
"c"
I tried implementing this manually, but I ran into some walls. Here is what I tried:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Label where
class (Show a) => Label a where
label :: a -> String
instance Label [Char] where
label str = str
instance Label Char where
label c = [c]
-- Default case
instance Show a => Label a where
label x = show x
However, because the default case's class overlaps instance Label [Char]
and instance Label Char
, those types don't work with the label
function.
Is there a library function that provides this functionality? If not, is there a workaround to get the above code to work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
上面的代码不起作用,因为仅根据“头”(即类名后面的部分)选择实例。 “上下文”,即
=>
之前的内容,例如“Show a”,仅在之后进行检查。上下文可以消除一个实例并产生编译器错误,但不会导致编译器选择不同的实例。由于这种行为,重叠实例可能会产生歧义。有一些编译器扩展可以让您编写更复杂的实例,但我怀疑您最好只编写
Label
类的单个实例。您这样做的目的是什么?根据您想要完成的任务,可能已经有一些更特殊用途的东西了。不过,您的示例代码非常简单 - 如果您愿意,只需添加
OverlappingInstances
扩展即可使其无需进一步修改即可工作。使用 OverlappingInstances 会导致 GHC 容忍一些歧义,只要存在明显的“最具体”实例即可。在您的代码中,具有具体类型的两个实例是非常具体的,因此应该不会有任何问题。也可以在使用时添加
TypeSynonymInstances
,以获得更好的可读性:The code above isn't going to work because instances are chosen only based on the "head", that is, the part after the class name. The "context", the stuff before the
=>
such as `Show a' is only examined afterwards. The context can eliminate an instance and produce a compiler error, but not cause the compiler to pick a different instance. Because of this behavior, overlapping instances are a potential ambiguity.There are compiler extensions that can let you write more complicated instances, but I suspect you're probably best off just writing individual instances of your
Label
class. What purpose do you have in mind for this? Depending on what you're trying to accomplish, there might be something more special-purpose already out there.Your example code is pretty simple, though--if you want, simply adding the
OverlappingInstances
extension should make it work with no further modifications. UsingOverlappingInstances
causes GHC to tolerate some ambiguity, so long as there's an obvious "most specific" instance. In your code, the two instances with concrete types are as specific as it gets, so there shouldn't be any problems.Might as well add
TypeSynonymInstances
while you're at it, for better readability:有一个
OverlappingInstances
语言扩展可以实现这一点。There's an
OverlappingInstances
language extension which will make this work.是的。有一个相当新的库提供了有用的功能,例如
toS
,其使用方式与show
类似。 (查看文档 )它可以与 string-conv 包下的 cabal 一起安装,如下所示:
cabal install string-conv
参考:
黑客攻击
Yes. There's a fairly new library that provides helpful functions, such as
toS
, which can be used similarly toshow
. (see docs)It can be installed with cabal under the string-conv package like so:
cabal install string-conv
Reference:
Hackage
这并不是您真正想要的,因为它为类型(Typeable)添加了额外的约束
但这是一般情况下可以做到的方法:
Data.Generics> (显示 `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) 1
"1"
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) "hello"
"hello"
Data.Generics> (显示 `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) 'c'
"c"
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) ['f','l']
"fl"
Data.Generics> :t (显示 `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String))
(显示 `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String))
::(显示a,可输入a)=>一个->细绳
Not really what you want, since it adds an extra constraint to the type (Typeable)
but this is how you could do it generically:
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) 1
"1"
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) "hello"
"hello"
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) 'c'
"c"
Data.Generics> (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String)) ['f','l']
"fl"
Data.Generics> :t (show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String))
(show `extQ` (id :: String -> String) `extQ` ((:[]) :: Char -> String))
:: (Show a, Typeable a) => a -> String