data A = Num Int
| Fun (A -> A) String deriving Show
instance Show (Fun (A -> A) String) where
show (Fun f s) = s
我希望函数A -> A
有一个属性来打印它,因此String
有一个Fun
类型参数。当我把它加载到ghci时,我得到了
/home/kmels/tmp/show-abs.hs:4:16:
Not in scope: type constructor or class `Fun'
我想这可以通过添加新的数据类型来实现
data FunWithAttribute = FA (A -> A) String
添加data A = Num Int | Fun FunWithAttribute
并撰写instance Show FunWithAttribute
。附加数据类型是否可以避免?
答案 0 :(得分:11)
实例是为类型定义的,而不是单个构造函数,这就是为什么它抱怨Fun
不是类型。
我假设您的总体目标是为Show
设置一个A
实例,这个实例无法派生,因为函数不能(通常)具有Show
实例。你有几个选择:
Show
实例:就是这样:
instance Show A where
show (Num n) = "Num " ++ show n
show (Fun _ s) = s
在许多情况下,这是最有意义的。但有时导出Show
会更好,特别是在复杂的递归类型中,只有一个案例中的一个不能自动Show
能够。{/ p>
A
可导出:您只能为包含自己拥有Show
个实例的类型的类型派生Show
。 A -> A
没有实例,因此派生不起作用。但是你可以编写一个使用某种占位符的文件:
instance Show (A -> A) where
show _ = "(A -> A)"
如果您愿意,甚至只是一个空字符串。
请注意,这需要the FlexibleInstances
language extension;它是最无害和最常用的扩展之一,由多个Haskell实现支持,它放松的限制(在我看来)开始时有点傻,所以没有理由避免它。
另一种方法是使用包装类型,正如您在问题中提到的那样。你甚至可以使它更通用:
data ShowAs a = ShowAs a String
instance Show (ShowAs a) where
show (ShowAs _ s) = s
...然后在(ShowAs (A -> A))
构造函数中使用Fun
。这使得在你想要使用包装类型的任何时候强迫你进行额外的模式匹配会让你有点尴尬,但是它给你很大的灵活性来“标记”它应该如何显示的东西,例如showId = id `ShowAs` "id"
或类似的。
答案 1 :(得分:5)
也许我没有按照你的要求去做。但是上面的代码可以这样写,以便编译:
data A = Num Int
| Fun (A -> A) String
instance Show A where
show (Fun f s) = s
show (Num i) = show i
看起来你正在尝试为构造函数(Fun)编写一个show实例。为整个数据类型编写类实例(可能是例外,dunno)。因此,您需要在每个构造函数上编写一个匹配的show作为实例的一部分。 Num和Fun是数据类型A的每个构造函数。
此外,除非每个构造函数的每个参数都是deriving
的成员,否则不能使用Show
。现在,您的示例有点特殊,因为它想要Show (A -> A)
。如何展示一个功能在其他回复中有所解释,尽管我认为没有详尽的方法。其他例子实际上只是“显示”类型或某个占位符。
答案 2 :(得分:4)
需要为数据类型定义Show
实例(或任何类实例),而不是为类型构造函数定义。也就是说,你只需要
instance Show A where
显然,您正在尝试使用deriving
获取此实例,但这不起作用,因为Haskell不知道如何显示A->A
。现在,您似乎甚至想要来显示该功能,但deriving Show
个实例始终显示所有可用信息,因此您无法使用它。 / p>
对您的问题的明显且最好的解决方案是worldsayshi:不要使用deriving
,而是自己定义一个合适的实例。或者,您可以为A->A
定义伪实例,然后使用deriving:
{-# LANGUAGE FlexibleInstances #-}
data A = Num Int | Fun (A->A) String deriving(Show)
instance Show (A->A) where show _ = ""
这就像
一样Prelude> Fun (const $ Num 3) "bla"
Fun "bla"