2

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

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

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

{-# 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 无法推断要使用哪个实例(一个用于颜色或一个用于灰度)。那是对的吗?如果是这样,我将如何解决这个问题?


注意ColourGrey和的定义ColourRGB超出了我的影响范围(它们是外部库的一部分)。所以任何建议都需要以这两种类型为模。我也不想乱用ColourStream,因为它在许多其他地方都使用过。

我需要访问“原始”列表的原因是我可以使用诸如map等之类的东西。如果有一个我还没有学习的巧妙技巧可以使ColourStream迭代,那么我想那会奏效......</thinking aloud>

4

3 回答 3

4

问题是 Haskell 看到extractStreamData并意识到它会返回一些Colour a => a,但是你将它输入到 show 中,所以它也意识到这a是 的一个实例Show,但在那之后就没有提到a

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

print $ (extractStreamData someGreys :: [ColourGrey])

像那样。

但是,您的 typeclass 实例有点……令人担忧。如果您将 a 传递ColourGrayColourRGB实例会发生什么?在运行时炸毁几乎不知道的信息是一个糟糕的反应。

请记住,类型类只是一种[真正加强]基于类型重载的方法。真的,看起来你想要丹尼尔瓦格纳所拥有的或类似的东西

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

然后让你的实例更像

instance Colour ColourGrey where
  extractStreamData (Greys x) = x
  extractStreamData (Colours x) = map translateRGB x

这样您只需选择是否要将流视为ColourGreys 或ColourRGBs 并使用它。现在,当您使用时,extractStreamData您不会首先尝试找出流中的内容。

于 2013-10-31T16:54:51.007 回答
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."
于 2013-10-31T17:17:49.330 回答
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

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

于 2013-10-31T17:27:04.353 回答