4

我想要实现的是,以下类 ( SampleSpace) 的任何实例都应该自动成为 的实例Show,因为SampleSpace包含创建 String 表示所需的整个接口,因此该类的所有可能实例实际上都是相同的。

{-# LANGUAGE FlexibleInstances #-}
import Data.Ratio (Rational)                                               

class SampleSpace space where                                               
    events          :: Ord a => space a -> [a]                              
    member          :: Ord a => a -> space a -> Bool                        
    probability     :: Ord a => a -> space a -> Rational                    

instance (Ord a, Show a, SampleSpace s) => Show (s a) where                 
    show s = showLines $ events s                                           
        where                                                               
        showLines [] = ""                                                   
        showLines (e:es) = show e ++ ":   " ++ (show $ probability e s)
                                  ++ "\n" ++ showLines es

因为,正如我已经发现的那样,在匹配实例声明时,GHC 只查看头部,而不是约束,因此它认为Show (s a)也是关于 Rational:

[1 of 1] Compiling Helpers.Probability ( Helpers/Probability.hs, interpreted )

Helpers/Probability.hs:21:49:
    Overlapping instances for Show Rational
      arising from a use of ‘show’
    Matching instances:
      instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      instance (Ord a, Show a, SampleSpace s) => Show (s a)
        -- Defined at Helpers/Probability.hs:17:10
    In the expression: show
    In the first argument of ‘(++)’, namely ‘(show $ probability e s)’
    In the second argument of ‘(++)’, namely
      ‘(show $ probability e s) ++ "" ++ showLines es

问题:是否有可能(除了启用重叠实例)使类型类的任何实例也自动成为另一个类型的实例?

4

1 回答 1

9

tl; dr:不要那样做,或者,如果你坚持,使用-XOverlappingInstances.

  1. 这不是Show上课的目的。Show用于简单地显示普通数据,以一种实际上是 Haskell 代码的方式,可以再次使用,产生原始值。
  2. SampleSpace或许一开始就不应该是一堂课。它似乎基本上是Map a Rational与它们相关联的类型的类。为什么不将其用作普通data类型的字段?
  3. 即使我们接受这个设计......Show当有人为具体类型创建另一个实例时,这样的泛型实例(或者实际上是任何单参数类的泛型实例)也会遇到问题——在 的情况下Show,当然已经有周围有很多实例。那么编译器应该如何决定使用这两个实例中的哪一个呢?事实上,GHC 可以做到这一点:如果您打开-XOverlappingInstances扩展程序,它将选择更具体的扩展程序(即被instance SampleSpace s => Show (s a)任何更具体的实例“覆盖”),但实际上这并不像看起来那么简单——如果有人定义了另一个这样的通用实例?重要的回忆:Haskell 类型的类总是 open,即基本上编译器必须假设所有类型可能在任何类中。只有当一个特定的实例被调用时,它才会真正需要证明,但它永远不能证明一个类型不在某个类中。

我推荐的是——因为该Show实例不仅仅显示数据,它应该是一个不同的函数。任何一个

showDistribution :: (SampleSpace s, Show a, Ord a) => s a -> String

或者确实

showDistribution :: (Show a, Ord a) => SampleSpace a -> String

whereSampleSpace是单个具体类型,而不是类。

于 2015-03-21T14:46:56.630 回答