跨类型的模式匹配

时间:2013-10-31 16:45:54

标签: haskell pattern-matching typeclass

我是Haskell的新手,所以很可能我错过了一些明显的东西......

我正在尝试使用ad hoc多态来编写通用颜色量化算法。但是,我在使用模式匹配获取数据时遇到了一些麻烦(我实际上还没有得到量化位)。

我无法简洁地描述这一点,所以这是我的代码的简化版本,它展示了这个问题:

{-# LANGUAGE FlexibleInstances #-}

import Data.Word

type ColourGrey = Word8

data ColourRGB = ColourRGB Word8 Word8 Word8
  deriving (Show)

data ColourStream = Greys   [ColourGrey]
                  | Colours [ColourRGB]
                    deriving (Show)

class Colour a where
  extractStreamData :: ColourStream -> [a]

instance Colour ColourGrey where
  extractStreamData (Greys x) = x

instance Colour ColourRGB where
  extractStreamData (Colours x) = x

someColours = Colours [ColourRGB 255 0 0, ColourRGB 0 255 0, ColourRGB 0 0 255]
someGreys   = Greys   [0, 127, 255]

main :: IO ()
main = do
  print $ extractStreamData someColours
  print $ extractStreamData someGreys

extractStreamData的调用失败,并显示以下内容:

No instance for (Colour a1)
  arising from a use of `extractStreamData'
The type variable `a1' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance Colour ColourRGB -- Defined at test.hs:20:10
  instance Colour ColourGrey -- Defined at test.hs:17:10
In the second argument of `($)', namely
  `extractStreamData someGreys'
In a stmt of a 'do' block: print $ extractStreamData someGreys
In the expression:
  do { print $ extractStreamData (someColours :: ColourStream);
       print $ extractStreamData someGreys }

对我来说,这意味着Haskell无法推断使用哪个实例(颜色的那个或灰度的实例)。那是对的吗?如果是这样,我将如何解决这个问题?


n.b。 ColourGreyColourRGB的定义超出了我的影响范围(它们是外部库的一部分)。因此,任何建议都需要以这两种类型为模。我也不想惹恼ColourStream,因为它在许多其他地方使用过。

我需要访问“原始”列表的原因是我可以使用map等等。如果有一个巧妙的技巧我还没有学会哪个会使ColourStream可迭代,那么我认为这样可行...... </thinking aloud>

3 个答案:

答案 0 :(得分:4)

问题是Haskell看到extractStreamData并意识到它将返回一些Colour a => a,但是然后你将其传递给show,这样它也意识到a是{{1}的一个实例1}},但之后没有提到Show

这意味着Haskell无法确定a应该是哪种类型。解决方案只是帮助typechecker并添加一些签名

a
像那样。

但是你的类型类实例有点......令人担忧。如果您通过了print $ (extractStreamData someGreys :: [ColourGrey]) 传递给ColourGray实例,会发生什么?在运行时爆炸而几乎不知道信息是一种不好的反应。

请记住,类型类只是一种基于重载的[真正加强]类型的方式。真的看起来你想要像Daniel Wagner那样的东西

ColourRGB

然后让你的实例更像

translateRGB :: ColourRGB -> ColourGrey
translateGrey  :: ColourGrey -> ColourRGB

这样您就可以选择是将广告信息视为instance Colour ColourGrey where extractStreamData (Greys x) = x extractStreamData (Colours x) = map translateRGB x 还是ColourGrey,并使用它。现在当你使用ColourRGB时,你并不是想先弄清楚流中的内容。

答案 1 :(得分:1)

我怀疑,虽然我不确定,你真正想要的是这样的:

onColourStream ::
    ([ColourGrey] -> [ColourGrey]) ->
    ([ColourRGB ] -> [ColourRGB ]) ->
    (ColourStream -> ColourStream)
onColourStream onGreys onRGBs (Greys gs) = Greys (onGreys gs)
onColourStream onGreys onRGBs (Colours rgbs) = Colours (onRGBs rgbs)

如果ColourGreyColourRGB分享某些操作 - 例如,闪电操作 - 并且您希望在您拥有的任何内容上使用这些操作,然后您可能创建一个类型类并执行以下操作:

class Colour a where lighten :: Double -> a -> a
instance Colour ColourRGB  where lighten = ...
instance Colour ColourGrey where lighten = ...

onColourStreamPoly ::
    (forall a. Colour a => [a] -> [a]) ->
    (ColourStream -> ColourStream)
onColourStreamPoly f = onColourStream f f

然后你可以编写类似onColourStreamPoly (map (lighten 0.5))左右的东西,将所有颜色减半。

编辑:回应“我最终想要做的是将图像颜色数据流(RGB或灰色)与调色板(另一个始终与图像的颜色空间匹配的流)匹配”:在这种情况下,我认为最轻的事情就是使用模式匹配。假设您有一些函数,如:

matchPalette :: Eq a => [a] -> [a] -> [Int]
myAlgorithm  :: [Int] -> [Int]
unmatchPalette :: [Int] -> [a] -> [a]

然后你可以这样做:

pipeline :: Eq a => [a] -> [a] -> [a]
pipeline xs ys = unmatchPalette (myAlgorithm (matchPalette xs ys)) xs

wholeShebang :: ColourStream -> ColourStream -> ColourStream
wholeShebang (Greys gs) (Greys gs') = Greys (pipeline gs gs')
wholeShebang (Colours rgbs) (Colours rgbs') = Colours (pipeline rgbs rgbs')
wholeShebang _ _ = error "Yikes! The colour stream and palette didn't match up."

答案 2 :(得分:0)

这是一个更简单的程序版本,没有编译问题:

import Data.Word

type ColourGrey = Word8

data ColourRGB = ColourRGB Word8 Word8 Word8
  deriving (Show)

data ColourStream a = ColourStream [a]

extractStreamData (ColourStream stream) = stream

someColours = ColourStream [ColourRGB 255 0 0, ColourRGB 0 255 0, ColourRGB 0 0 255]
someGreys   = ColourStream ([0, 127, 255] :: [ColourGrey])

main :: IO ()
main = do
  print $ extractStreamData someColours
  print $ extractStreamData someGreys

当然,我不知道你将如何在这个简单的例子之外使用这些定义,所以我不确定它是否足以满足你的目的。