首页 文章

使类型类实例自动成为另一个的实例

提问于
浏览
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

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

1 回答

  • 9

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

    • 这不是 Show 类的用途 . Show 用于简单地显示纯数据,实际上是Haskell代码并且可以再次使用,产生原始值 .

    • SampleSpace 首先应该不是一个 class . 它似乎基本上是与类似 Map a Rational 之类的类型 . 为什么不将它用作普通 data 类型的字段?

    • 即使我们接受了设计......这样一个通用的 Show 实例(或者,实际上是任何单参数类的通用实例)当有人为具体类型创建另一个实例时遇到问题 - 在 Show 的情况下,有当然已经有很多实例了 . 那么编译器应该如何决定使用哪两个实例呢?事实上GHC可以做到这一点:如果你打开 -XOverlappingInstances 扩展名,它会选择更具体的扩展名(即 instance SampleSpace s => Show (s a) 被任何更具体的实例“覆盖”),但实际上这并不像看起来那么简单 - 什么如果有人定义了另一个这样的通用实例?至关重要的回忆:Haskell类型类总是开放的,即基本上编译器必须假设所有类型都可能在任何类中 . 只有在调用特定实例时,它才真正需要证明,但它永远不会证明类型不在某个类中 .

    我推荐的是 - 因为 Show 实例不仅仅显示数据,它应该是一个不同的功能 . 或

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

    或者确实

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

    其中 SampleSpace 是单个具体类型,而不是类 .

相关问题