Haskell实例:这怎么可能是一些有效的代码?

在我编写 Show 实例的一个小例子时,我犯了一个缩进错误:

module Main where

data B= B0|B1 

instance Show B where
show B0="0"
show B1="1"


main=print B0

显然,工作代码是:

module Main where

data B= B0|B1 

instance Show B where
    show B0="0"
    show B1="1"


main=print B0

我原以为第一个会出现编译错误,但我可以运行它,结果是:

example.hs: stack overflow

为什么这段代码甚至可以编译?

另外,为什么这只是运行时错误(如果堆栈不受约束,则会填满您的 RAM)而不是编译错误?

回答

an 的主体instance可以是空的。您可以省略以下where条款:

instance Show B

但你也可以包括它:

instance Show B where
-- nothing here

这对于为方法提供默认实现的类型类很有用,可能基于泛型编程工具。例如,对于aeson包,定义实例与 JSON 之间的转换的常用方法是使用泛型和空实例:

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics

data Person = Person {
      name :: Text
    , age  :: Int
    } deriving (Generic, Show)

-- these empty instances use Generics to provide a default implementation
instance ToJSON Person
instance FromJSON Person

在您的程序中,通过省略缩进,您定义了一个instance Show B没有方法定义的方法(并且-Wall会生成一个“缺少方法”警告,告诉您它不满足实例的最低要求)。unindentedshow为 提供了一个新的顶级定义show,与类型类show中的无关Show

你没有show明确使用。相反,您通过 隐式使用了它print,它总是show从类型类调用,忽略您的顶级定义,因此您的崩溃程序等效于:

data B = B0 | B1
instance Show B
main = print B0

这会产生堆栈溢出,因为当没有给出特定实例时,会使用show和 的默认定义showsPrec

show x = shows x ""
showsPrec _ x s = show x ++ s

与顶级函数shows(不是类型类的一部分)一起操作:

shows = showsPrec 0

这在实例中至少定义了一个showor时效果很好showsPrec,然后另一个得到了合理的定义,但如果两者都没有定义,这会在这三个函数之间创建一个无限递归循环。

另外,请注意,以下程序会告诉您show是模棱两可的,这将使您更清楚发生了什么。

module Main where

data B= B0|B1 

instance Show B where
show B0="0"
show B1="1"

main=putStrLn (show B0) -- instead of print


回答

由于您没有缩进show,这意味着这不属于instancefor Show,因此您的程序相当于:

instance Show B where  -- ← no implementations

show B0 = "0"
show B1 = "1"

因此show,您在这里构造了另一个与Show类型类无关的函数,而只是具有相同的名称。

Show定义了三种功能showsPrec :: Show a => Int -> a -> String -> Stringshow :: a -> String -> StringshowList :: [a] -> String -> StringshowsPrec经常使用,因为它具有使用括号的优先级系统。

因此,show在以下方面受到影响showsPrecshowsPrec在以下方面实施show

class  Show a  where
    {-# MINIMAL showsPrec | show #-}
    -- …
    showsPrec :: Int
              -> a
              -> ShowS
    -- …
    show      :: a   -> String
    -- …
    showList  :: [a] -> ShowS

    showsPrec _ x s = show x ++ s
    show x          = shows x ""
    showList ls   s = showList__ shows ls s

-- …
shows           :: (Show a) => a -> ShowS
shows           =  showsPrec 0

因此,实现showsPrecor就足够了show,这就是为什么{-# MINIMUM showsPrec | show #-}在类型类的顶部有一个编译指示。

如果您没有实现这两者中的任何一个,那么编译器将发出警告,并因此使用基本实现,但这将导致什么都没有,因为show将调用showPrec,然后调用show,直到最终堆栈耗尽。


以上是Haskell实例:这怎么可能是一些有效的代码?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>