我是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。 ColourGrey
和ColourRGB
的定义超出了我的影响范围(它们是外部库的一部分)。因此,任何建议都需要以这两种类型为模。我也不想惹恼ColourStream
,因为它在许多其他地方使用过。
我需要访问“原始”列表的原因是我可以使用map
等等。如果有一个巧妙的技巧我还没有学会哪个会使ColourStream
可迭代,那么我认为这样可行...... </thinking aloud>
答案 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)
如果ColourGrey
和ColourRGB
分享某些操作 - 例如,闪电操作 - 并且您希望在您拥有的任何内容上使用这些操作,然后您可能创建一个类型类并执行以下操作:
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
当然,我不知道你将如何在这个简单的例子之外使用这些定义,所以我不确定它是否足以满足你的目的。