跨类型类的模式匹配 [英] Pattern matching across type classes
问题描述
我想用ad hoc编写一个通用的颜色量化算法多态性。然而,我在使用模式匹配来获取数据方面遇到了一些麻烦(我实际上还没有进入量化位)。
我无法描述这个简洁的,所以这里是我的代码的简化版本,它展示了这个问题:
{ - #LANGUAGE FlexibleInstances# - }
导入Data.Word
类型ColourGrey = Word8
data ColourRGB = ColourRGB Word8 Word8 Word8
派生(显示)
data ColourStream = Grays [ColourGrey]
|颜色[ColourRGB]
派生(显示)
类颜色a其中
extractStreamData :: ColourStream - > [a]
实例Color ColourGrey其中
extractStreamData(Grays x)= x
实例Color ColourRGB其中
extractStreamData(Colors x)= x
someColours =颜色[ColourRGB 255 0 0,ColourRGB 0 255 0,ColourRGB 0 0 255]
someGreys =灰色[0,127,255]
main: :IO()
main = do
print $ extractStreamData someColours
print $ extractStreamData someGreys
对 extractStreamData
的调用会失败,如下所示:
没有使用`extractStreamData'引发的(Color a1)
的实例
类型变量`a1'不明确
可能的修正:添加修复这些类型变量的类型签名( s)
注意:有几个潜在的实例:
实例Color ColourRGB - 定义在test.hs:20:10
实例Color ColourGrey - 定义在test.hs:17:10
在`($)'的第二个参数中,即
`extractStreamDa ta someGreys'
在'do'块的标记中:print $ extractStreamData someGreys
在表达式中:
do {print $ extractStreamData(someColours :: ColourStream);
print $ extractStreamData someGreys}
对我而言,这意味着Haskell无法推断要使用哪个实例(颜色或灰度)。这是对的吗?如果是这样,我该如何解决这个问题?
nb c $ c> ColourGrey 和 ColourRGB
不在我的影响范围之内(它们是外部库的一部分)。所以任何建议都需要模仿这两种类型。我不想惹恼 ColourStream
,因为它在很多其他地方都有使用过。
我需要访问原始列表的原因是我可以使用诸如 map
等的东西。如果有一个巧妙的技巧,我还没有学习哪些将会使 ColourStream
iterable,那么我想这可能会奏效...... < /> />
问题是Haskell看到 extractStreamData
并意识到它将返回一些颜色a =>一个
,但然后你管它进入显示,所以它也意识到 a
是一个 Show $ c的实例$ c>,但之后没有提及
a
。
这意味着Haskell无法弄清楚哪种类型的 a
应该是。解决方案只是帮助typechecker并添加一些签名。
$ print $(extractStreamData someGreys :: [ColourGrey])
类似。
然而,你的类型类实例是有点...令人担忧。如果你通过了 ColourGray
到 ColourRGB
实例,会发生什么?请注意,类型类只是一种基于类型的重载的方法。真的,它看起来像你想像丹尼尔瓦格纳有什么或类似的东西
translateRGB :: ColourRGB - > ColourGrey
translateGrey :: ColourGrey - > ColourRGB
然后让您的实例更像是
实例Color ColourGrey其中
extractStreamData(Grays x)= x
extractStreamData(Colors x)= map translateRGB x
这样您就可以选择是否将流视为 ColourGrey
s或 ColourRGB
s并使用它。现在,当您使用 extractStreamData
时,您并不是想先弄清楚流中的内容。
I'm a Haskell newbie, so chances are I've missed something obvious...
I am trying to write a generic colour quantisation algorithm using ad hoc polymorphism. However, I'm having some trouble getting my data out with pattern matching (I've not actually got to the quantisation bit, yet).
I can't describe this succinctly, so here's a simplified version of my code which exhibits the problem:
{-# 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
The calls to extractStreamData
fail with the following:
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 }
This implies, to me, that Haskell can't infer which instance to use (the one for colours or the one for greyscale). Would that be right? If so, how would I go about fixing this?
n.b. The definitions of ColourGrey
and ColourRGB
are outside my scope of influence (they're part of an external library). So any suggestions need to be modulo these two types. I'd prefer not to mess about with ColourStream
, either, as it's used in lots of other places.
The reason I need to access the "raw" lists is so I can work with things like map
, etc. If there's a neat trick I've yet to learn which will make ColourStream
iterable, then I suppose that would work... </thinking aloud>
The problem is that Haskell sees extractStreamData
and realizes it's going to return some Colour a => a
, but then you pipe that into show so it also realizes that a
is an instance of Show
, but after that there's no mention of a
.
This means that Haskell can't figure out which type a
should be. The solution is just to help out the typechecker and add some signatures
print $ (extractStreamData someGreys :: [ColourGrey])
like that.
However your typeclass instances are a bit... worrisome. What would happen if you passed you passed a ColourGray
to the ColourRGB
instance? Blowing up at runtime with little to know information is a bad response.
Remember that typeclasses is just a way of doing [really souped up] type based overloading. Really it looks like you want like what Daniel Wagner has or something like
translateRGB :: ColourRGB -> ColourGrey
translateGrey :: ColourGrey -> ColourRGB
And then make your instances more like
instance Colour ColourGrey where
extractStreamData (Greys x) = x
extractStreamData (Colours x) = map translateRGB x
so that you just choose whether you want to treat your stream as ColourGrey
s or ColourRGB
s and work with that. Now when you use extractStreamData
you're not trying to figure out what's in the stream first.
这篇关于跨类型类的模式匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!