我正在尝试构造一个本质上是二叉树的数据类型,其中:每个节点的左分支是一个函数,可以作用于每个节点的右分支中的变量。我是Haskell的新手,我不确定我是否会以正确的方式解决这个问题,但我目前的问题是我无法弄清楚如何将我的类型添加到Show类型类中。这是我的尝试:
{-# LANGUAGE ExistentialQuantification #-}
-- file: TS.hs
data TypeSentence a = forall b. Apply (TypeSentence (b->a)) (TypeSentence b)
| Expr a
instance (Show a) => (Show (TypeSentence a)) where
show (Expr x) = show x
show (Apply x y) = (show x) ++ " " ++ (show y)
instance (Show (TypeSentence b->a)) where
show (Expr x) = show "hello"
x = Expr 1
f = Expr (+1)
s = Apply f x
但是,当我将其加载到ghci时,我收到以下错误:
TS.hs:9:24:
Could not deduce (Show (b -> a)) from the context ()
arising from a use of `show' at TS.hs:9:24-29
Possible fix:
add (Show (b -> a)) to the context of the constructor `Apply'
or add an instance declaration for (Show (b -> a))
In the first argument of `(++)', namely `(show x)'
In the expression: (show x) ++ " " ++ (show y)
In the definition of `show':
show (Apply x y) = (show x) ++ " " ++ (show y)
Failed, modules loaded: none.
我是如何添加Show(b-> a)声明的?
感谢。
答案 0 :(得分:7)
您的代码存在一些问题,因此我将逐一介绍它们。
您无法为Show (a -> b)
添加特别丰富的实例。考虑一下如何编写它:
instance Show (a -> b) where
show f = error "What goes here?"
由于f
是一个函数,除了将它应用于某个值之外,你无法用它做任何事情。由于a
是完全多态的类型,因此无法创建a
类型的值来应用f
。所以你唯一的选择就是
instance Show (a -> b) where
show _ = "<function>"
Daniel Fischer在评论中说,这可以在Text.Show.Functions模块中找到。不过,我真的不打扰这个;我只想写一些像
的东西instance Show a => Show (TypeSentence a) where
show (Apply _ x) = "Apply _ " ++ show x -- This still won't work; see below
show (Expr x) = "Expr " ++ show x
由于show
只能为任何函数返回一个字符串,所以只能直接内联。
尽管如此,你仍然无法写出Show
个实例。如果您尝试编译上面的实例,则会收到以下错误:
TS.hs:8:36:
Could not deduce (Show b) arising from a use of `show'
from the context (Show a)
bound by the instance declaration
at TS.hs:7:10-40
Possible fix:
add (Show b) to the context of
the data constructor `Apply'
or the instance declaration
In the second argument of `(++)', namely `show x'
In the expression: "Apply _ " ++ show x
In an equation for `show': show (Apply _ x) = "Apply _ " ++ show x
问题在于,在您TypeSentence
的定义中,Apply
隐藏x
show
定义中的变量(TypeSentence
绑定})由一些任意的存在隐藏类型b
参数化。但是不能保证b
可以显示,因此show x
不会输入检查,这是上面产生的错误:Show b
没有实例,因为b
是任意的。所以要摆脱它,最简单的方法是
instance Show a => Show (TypeSentence a) where
show (Apply _ _) = "Apply _ _"
show (Expr x) = "Expr " ++ show x
这并不是特别有用。因此,Show
可能没有一个好的TypeSentence
实例。 (这没关系。许多有用的类型都没有Show
个实例。)
这个与其他一切无关。 instance Show (TypeSentence b -> a)
声明尝试为Show
到TypeSentence b
的函数声明a
的实例;如果您将其重新表示为instance Show (TypeSentence (b -> a))
,则仍需要FlexibleInstances
和OverlappingInstances
扩展名来进行编译。所以你应该只是斧头。
答案 1 :(得分:4)
好吧,让我们理解这一点。您将使用某个函数Show
调用您建议的show
实例的f :: b -> a
方法。
instance Show (b -> a) where
show f = ...
您的show
方法可以做什么?好吧,它必须产生一些String
,但它将如何做呢?
好吧,由于f
的类型为b -> a
,因此f
唯一可以将其应用于b
类型的内容。但是show
没有b
类型的参数,而您的Show
类没有类型b
的任何常量,所以show
唯一的问题就是f
使用undefined
的方法可以将其应用于f
。哪些可能会产生错误,也可能不会产生错误,具体取决于show
是否严格 - 您无法控制,我确定您不希望f undefined
错误地输出某些参数反正。
但无论如何,即使您确实从a
获得了结果,此结果也会显示a
类型,而您的定义对a -> whatever
没有任何影响。无论如何,因为你没有whatever
类型的任何功能。 (如果你确实有一个,除非String
是f
,否则你仍然处于相同的位置。)
所以你对f
无能为力,而且由于你没有其他参数,这意味着你的方法唯一可以做的就是返回一个不依赖于undefined
的值或任何其他论点。因此,您的方法的返回值必须是常量,或undefined
。由于使用show
会很愚蠢,因此String
方法唯一明智的做法就是返回常量instance Show (b -> a) where
show _ = "<function>"
:
Text.Show.Functions
正如Daniel Fischer在对你的问题的评论中提到的那样,这已经在foo :: (a -> b) -> [a] -> [b]
中提供了。
但这里的教训是以此为例说明如何推理你的问题。这是关于Haskell的一个巧妙的事情:你可以通过查看类型来证明函数可以,不能或必须做什么。例如,如果您有foo
,假设undefined
不够愚蠢,则无法免费使用b
,您可以推断[b]
中的a -> b
s [a]
通过将foo
类型参数应用于b
参数的元素来获得结果。 map :: (a -> b) -> [a] -> [b]
没有其他方法可以生成{{1}}类型的值。 (如果您没有猜到,那么该类型最自然的功能是{{1}}。)
答案 2 :(得分:0)
如果函数的域是有限集,那么您可以在所有点上打印函数的值。在Haskell中,您可以使用类似的函数对类型Ix
和Bounded
执行此操作:
rangeF :: (Ix a, Bounded a) => [a]
rangeF = range (minBound, maxBound)
答案 3 :(得分:0)
我认为@Davorak的评论就是您想要的。
https://stackoverflow.com/a/15846061/6289448
我只是在这里分享。通过ghc 8.6.5
中的测试。
对于使用Data.Typeable的所有函数,有一个部分解决方案不只是固定字符串。
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Typeable
instance (Typeable a, Typeable b) => Show (a->b) where
show _ = show $ typeOf (undefined :: a -> b)
以ghci
> let test :: Int->Int; test x = x + x
> test
Int -> Int
不幸的是,如果没有类型签名,该类型将变为默认值。
> let test x = x + x
> test
Integer -> Integer
此解决方案适用于多种功能领域,因为a-> b-> c与a->(b-> c)相同,您也可以将其写为a-> d,其中d = b-> c。
> let m10 a b c d e f g h i j = a * b * c * d * e * f * g * h* i * j
> m10
Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer
-> Integer -> Integer -> Integer -> Integer
但是,如果未知函数的参数是否具有可键入的类,则此方法将不起作用。因此,当map(+1)将起作用时,map将不会起作用。
> map (+1)
[Integer] -> [Integer]
> map
<interactive>:233:1:
...
浏览Data.Data的内部结构和一个或两个实验后,似乎可以将其重构为更通用的形式,涵盖更多功能。