如何在Haskell中为b-> a创建一个Show实例?

时间:2012-06-28 19:24:10

标签: haskell

我正在尝试构造一个本质上是二叉树的数据类型,其中:每个节点的左分支是一个函数,可以作用于每个节点的右分支中的变量。我是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)声明的?

感谢。

4 个答案:

答案 0 :(得分:7)

您的代码存在一些问题,因此我将逐一介绍它们。

  1. 您无法为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只能为任何函数返回一个字符串,所以只能直接内联。

  2. 尽管如此,你仍然无法写出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个实例。)

  3. 这个与其他一切无关。 instance Show (TypeSentence b -> a)声明尝试为ShowTypeSentence b的函数声明a的实例;如果您将其重新表示为instance Show (TypeSentence (b -> a)),则仍需要FlexibleInstancesOverlappingInstances扩展名来进行编译。所以你应该只是斧头。

答案 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类型的任何功能。 (如果你确实有一个,除非Stringf,否则你仍然处于相同的位置。)

所以你对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中,您可以使用类似的函数对类型IxBounded执行此操作:

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的内部结构和一个或两个实验后,似乎可以将其重构为更通用的形式,涵盖更多功能。