2

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

4 回答 4

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绑定)。但是不能保证它是可显示的,所以不会类型检查,这是上面产生的错误:没有实例 for ,因为是任意的。所以要摆脱它,最简单的方法是xshowTypeSentencebbshow xShow bb

    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)声明试图声明一个Showfor 函数的实例 from TypeSentence bto a; 如果你把它重新括起来instance Show (TypeSentence (b -> a)),你仍然需要FlexibleInstancesOverlappingInstances扩展来编译它。所以你可能应该只是斧头。

于 2012-06-28T20:59:33.780 回答
4

好吧,让我们推理一下。您提议Show的实例的show方法将被调用一些函数f :: b -> a

instance Show (b -> a) where
    show f = ...

你的show方法能做什么?好吧,它必须产生一些String,但它会怎么做呢?

好吧,既然 typefb -> a,你唯一能做的f就是把它应用到 type 的东西上b。然而show没有 type 的参数b,并且你的Show类没有任何 type 的常量b,所以这个方法唯一show能做的f就是将它应用到undefined. 这可能会或可能不会产生错误,具体取决于是否f严格 - 您无法控制,而且我相信您无论如何都不想show在某些参数上出错。

但无论如何,即使你确实从 得到了一个结果f undefined,这个结果也会有 type a,而且你的定义真的不能用 ana做任何事情,因为你没有任何a -> whatever可用的 type 函数。(如果你确实有一个,除非whatever是,否则String你仍然处于相同的位置。)

所以你不能用 做任何明智的事情f,因为你没有其他参数,这意味着你的方法唯一能做的就是返回一个不依赖于f或任何其他参数的值。因此,您的方法的返回值必须是一个常量,或者undefined. 因为使用undefined会很傻,所以这个show方法唯一能做的就是返回一个常量String

instance Show (b -> a) where
    show _ = "<function>"

正如 Daniel Fischer 在他对您的问题的评论中提到的那样,这已经在Text.Show.Functions.

但这里的教训是以这个为例,说明如何通过你的问题进行推理。这是有关 Haskell 的巧妙之处之一:您通常可以通过查看类型来证明一个函数可以做什么、不能做什么或必须做什么。例如,如果您有foo :: (a -> b) -> [a] -> [b], 假设foo是不够傻的 不undefined无偿使用,您可以推断结果中的bs[b]是通过将a -> b类型参数应用于参数的元素来获得的[a]。没有其他方法foo可以产生 type 的值b。(如果您还没有猜到,该类型中最自然的函数是map :: (a -> b) -> [a] -> [b]。)

于 2012-06-28T20:43:37.450 回答
1

我认为@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 where 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 的内部结构和一两个实验之后,似乎可以对其进行重构,使其更加通用,涵盖更多功能。



如果你不喜欢上面的实现,就自己实现吧!(如果有更好的方法,请告诉我!)

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}

newtype GenType a =
    GenType
        { asTypeStr :: String
        }

class GenTypeArbitrary a where
    gtArbitrary :: a -> GenType a

instance GenTypeArbitrary String where
    gtArbitrary :: String -> GenType String
    gtArbitrary _ = GenType "String123"

instance GenTypeArbitrary Bool where
    gtArbitrary :: Bool -> GenType Bool
    gtArbitrary _ = GenType "Bool123"

instance GenTypeArbitrary Int where
    gtArbitrary :: Int -> GenType Int
    gtArbitrary _ = GenType "Int123"

instance (GenTypeArbitrary a, GenTypeArbitrary b) => GenTypeArbitrary (a -> b) where
    gtArbitrary :: (GenTypeArbitrary a, GenTypeArbitrary b) => (a -> b) -> GenType (a -> b)
    gtArbitrary _ = GenType $ aTypeStr' ++ " --> " ++ bTypeStr
      where
        aTypeStr = asTypeStr (gtArbitrary (undefined :: a))
        aTypeStr' =
            if "-->" `isInfixOf` aTypeStr
                then "(" ++ aTypeStr ++ ")"
                else aTypeStr
        bTypeStr = asTypeStr (gtArbitrary (undefined :: b))

instance  (GenTypeArbitrary a, GenTypeArbitrary b) => Show (a -> b) where
  show f = asTypeStr $ gtArbitrary f

test1 :: Int -> String
test1 x = ""

test2 :: Int -> String -> Int -> Bool -> Bool
test2 _ _ _ _ = False

test3 :: Int -> ((String -> Int) -> Bool) -> Bool
test3 _ _ = False

test4 :: Int -> (Bool -> (String -> Int)) -> Bool
test4 _ _ = False


λ > show  (test4)
    "Int123 --> (Bool123 --> String123 --> Int123) --> Bool123"
it :: String


...


λ > show  (test3)
    "Int123 --> ((String123 --> Int123) --> Bool123) --> Bool123"
it :: String


于 2019-07-29T01:54:08.543 回答
0

如果您的函数的域是有限集,那么您可以在所有点打印函数的值。在 Haskell 中,您可以使用类型类IxBounded使用如下函数来做到这一点:

rangeF :: (Ix a, Bounded a) => [a]
rangeF = range (minBound, maxBound)
于 2012-06-28T22:00:53.643 回答