实例实现的模式匹配

时间:2017-01-20 19:05:18

标签: haskell pattern-matching typeclass

我正在尝试实现数据类型的show方法。

data OptionList a b = EmptyOpt | OptionList { optListHead :: a, optListTail :: b } deriving (Read)

instance (Show a, Show b) => Show (OptionList a b) where
    show (OptionList a EmptyOpt) = "{" ++ (show a) ++"}"
    show (OptionList EmptyOpt b) = "{" ++ (show b) ++"}"
    show (OptionList a b) = "{"++ (show a) ++ ", " ++ (show b) ++"}"
    show EmptyOpt = ""

如果ab中的一个具有由EmptyOpt构造的值,我希望OptionList不显示逗号。但编译器显示以下错误:

OptionList.hs:11:28:
    Couldn't match expected type ‘b’
                with actual type ‘OptionList t0 t1’
      ‘b’ is a rigid type variable bound by
          the instance declaration at OptionList.hs:10:10
    Relevant bindings include
      show :: OptionList a b -> String (bound at OptionList.hs:11:9)
    In the pattern: EmptyOpt
    In the pattern: OptionList a EmptyOpt
    In an equation for ‘show’:
        show (OptionList a EmptyOpt) = "{" ++ (show a) ++ "}"

OptionList.hs:12:26:
    Couldn't match expected type ‘a’
                with actual type ‘OptionList t2 t3’
      ‘a’ is a rigid type variable bound by
          the instance declaration at OptionList.hs:10:10
    Relevant bindings include
      show :: OptionList a b -> String (bound at OptionList.hs:11:9)
    In the pattern: EmptyOpt
    In the pattern: OptionList EmptyOpt b
    In an equation for ‘show’:
        show (OptionList EmptyOpt b) = "{" ++ (show b) ++ "}"

UPDATE :OptionList应该类似于无类型列表。

(+:) :: a -> b -> (OptionList a b)
infixr 5 +:
t1 +: t2 = OptionList t1 t2

因此,像0 +: "test" +: True这样的列表定义为OptionList Int (OptionList String (OptionList Bool EmptyOpt))show n为{0, {"test", {True}}}

3 个答案:

答案 0 :(得分:6)

您的更新的更新。如果您愿意开启某些扩展程序,可以使其正常工作:

{-# LANGUAGE FlexibleInstances #-}

data EmptyOpt = EmptyOpt

data OptionList a b =
  OptionList a b
  deriving (Read)

instance (Show a, Show b) => Show (OptionList a b) where
  show (OptionList a b) = "{ " ++ show a ++ ", " ++ show b ++ " }"

instance {-# OVERLAPPING  #-} (Show a) => Show (OptionList a EmptyOpt) where
  show (OptionList a EmptyOpt) = "{ " ++ show a ++ " }"

(+:) :: a -> b -> (OptionList a b)
infixr 5 +:
t1 +: t2 = OptionList t1 t2

test = 0 +: "test" +: True +: EmptyOpt

但我个人会尝试用类似

的东西
data Option = B Bool | I Int | S String
data OptionsList = Empty | OptionsList Option OptionsList

您的问题是,实例主管((Show a, Show b) => Show (OptionList a b))表示您正在为Show实施OptionList a b,其中abShow的任何类型{ {1}}实例,但在您的实施中,您要求ab实际上都是OptionList类型。

也许您会将您的类型更改为更像普通列表:

data OptionList a
  = EmptyOpt
  | OptionList { optListHead :: a
              ,  optListTail :: OptionList a}
  deriving (Read)

然后你可以有一个实例:

instance (Show a) => Show (OptionList a) where
  show (OptionList a EmptyOpt) = "{" ++ show a ++"}"
  show (OptionList a b) = "{"++ show a ++ ", " ++ show b ++"}"
  show EmptyOpt = ""

答案 1 :(得分:3)

问题在于你的实例声明:

instance (Show a, Show b) => Show (OptionList a b) where
    show (OptionList a EmptyOpt) = "{" ++ (show a) ++"}"
    show (OptionList EmptyOpt b) = "{" ++ (show b) ++"}"
    show (OptionList a b) = "{"++ (show a) ++ ", " ++ (show b) ++"}"
    show EmptyOpt = ""

使用dataconstructors 。因此,Haskell正确地推导出你实际定义show超过OptionList (OptionList c d) (OptionList e f):毕竟EmptyOptOptionList a b的结构,所以你不能将它们用作参数,因为在您的实例的头部,您说您将使用通用OptionList a ba定义b的实例。

所以我不清楚你的目标是什么;将其修改为:

instance (Show a, Show b, Show c, Show d) => Show (OptionList (OptionList a b) (OptionList c d)) where

也无济于事,因为结构是递归的,因此您将定义具有无限递归深度的实例。

对我来说唯一合理的是你的数据定义是错误的,应该是这样的:

data OptionList a = EmptyOpt | OptionList { optListHead :: a, optListTail :: OptionList a }

在这种情况下,您可以将其定义为:

instance Show a => Show (OptionList a) where
    show (OptionList a b) = "{"++ show a ++ ", " ++ show b ++"}"
    show EmptyOpt = ""

或沿着这些方向的东西。

答案 2 :(得分:0)

我的想法是创建一个类型独立列表。这个列表的元素应该包含任何声明的类型。但我会放弃并做一些像C联盟的事情:

data Option = OptStr String | OptInt Int | OptBool Bool