Haskell,实例和类型之间的类型匹配问题 class

Haskell, problems matching types between an instance and a type class

我试图通过基于现有库 JuicyPixels.[=23 将整数列表转换为像素向量(具有相同的基础值但类型不同)来进行一些图像处理=]

但是当我试图让我的 pixel 成为图像时,发生了一个错误:像素的类型为 Pixel8,它是类型 class [=16] 的一个实例=],但是ghci告诉我它不能匹配这些类型:

    ? Couldn't match type ‘px’ with ‘Pixel8’
      ‘px’ is a rigid type variable bound by
        the type signature for:
          makeListImg :: forall px.
                         Pixel px =>
                         Int -> Int -> [Int] -> Image px
        at src\ImageHandling.hs:63:1-69
      Expected type: T.MutableImage s Pixel8 -> ST s (Image px)
        Actual type: T.MutableImage (PrimState (ST s)) px
                     -> ST s (Image px)
    ? In the second argument of ‘(>>=)’, namely ‘T.unsafeFreezeImage’
      In the expression:
        makeListMutableImg (w, h) (lst2px8 lst) >>= T.unsafeFreezeImage
      In an equation for ‘img’:
          img
            = makeListMutableImg (w, h) (lst2px8 lst) >>= T.unsafeFreezeImage
    ? Relevant bindings include
        img :: ST s (Image px) (bound at src\ImageHandling.hs:66:11)
        makeListImg :: Int -> Int -> [Int] -> Image px
          (bound at src\ImageHandling.hs:64:1)
   |
66 |           img = makeListMutableImg (w, h) (lst2px8 lst) >>= T.unsafeFreezeImage
   |                                                             ^^^^^^^^^^^^^^^^^^^

我的代码是(从第 63 行开始):

makeListImg :: forall px. Pixel px => Int -> Int -> [Int] -> Image px
makeListImg w h lst = runST img
    where img :: ST s (Image px)
          img = makeListMutableImg (w, h) (lst2px8 lst) >>= T.unsafeFreezeImage

lst2px8 :: [Int] -> [Pixel8]
lst2px8 = map toPixel8

makeListMutableImg :: forall m px. (Pixel px, PrimMonad m) => (Int, Int) -> [px] -> m (T.MutableImage (PrimState m) px)
makeListMutableImg (w, h) lst = T.MutableImage w h `liftM` vec
    where elemSize = T.componentCount (undefined :: px)
          vec = do
            arr <- M.new (w * h * elemSize)
            let drawLineFromList :: Int -> Int -> [px] -> m ()
                drawLineFromList _ y _ | y >= h = return ()
                drawLineFromList idx y list = column idx 0 list
                    where column :: Int -> Int -> [px] -> m ()
                          column i x l | x >= w = drawLineFromList i (y+1) l
                          column i x l@(head:tail) = do
                              T.unsafeWritePixel arr i head
                              column (i+elemSize) (x+1) tail
            drawLineFromList 0 0 lst
            return arr

这些代码太多了,对此我真的很抱歉,但我对自己在做什么并不清楚;我只是遵循 library.

中的想法

函数中monad和抽象层太多了,真不知道应该关注哪里。我想将列表复制到 MutableImage 中的可变向量,然后使用 unsafeFreezeImagerunST 将其转换为 Image px,但类型阻止了我。我现在该怎么办,我该如何解决这个问题?

我认为最好的方法是直接构建 Image。这只是一个常规数据类型:

data Image a = Image { imageWidth :: !Int
                     , imageHeight :: !Int
                     , imageData :: Vector (PixelBaseComponent a) }

其中 Vector 来自 Data.Vector.Storable.

对于 ImageRGB8PixelBaseComponent ImageRGB8Word8,而 Vector Word8 以行优先顺序排列,具有连续的每像素组件。从你的代码来看,这就是你的 [Int] 已经安排的方式,所以你的 makeListImg 函数应该像这样简单:

import Codec.Picture
import qualified Data.Vector.Storable as VS

makeListImg :: Int -> Int -> [Int] -> Image PixelRGB8
makeListImg w h = Image w h . VS.fromList . map fromIntegral

我无法想象 makeListImg 的多态版本会有用的应用程序,但可以定义如下内容:

unsafeMakeListImg :: (Integral (PixelBaseComponent p), VS.Storable (PixelBaseComponent p))
  => Int -> Int -> [Int] -> Image p
unsafeMakeListImg w h = Image w h . VS.fromList . map fromIntegral

这对于所有具有完整像素组件的图像格式来说已经足够通用了。尝试使用它的问题(以及我称之为“不安全”的原因)是 p 的推断类型将决定所提供列表 [Int] 结构的有效性。原始 makeListImg 也是不安全的,但至少它以可预测的方式不安全——你知道所需的 [Int] 参数始终是 Image PixelRGB8.

的数据

一个独立的例子:

import Codec.Picture
import qualified Data.Vector.Storable as VS

makeListImg :: Int -> Int -> [Int] -> Image PixelRGB8
makeListImg w h = Image w h . VS.fromList . map fromIntegral

main = do
  -- make a left-to-right red gradient
  let foo = makeListImg 100 100 $ concat [[156+x,0,0] | x <- [0..99], y <- [0..99]]
  saveJpgImage 75 "foo.jpg" $ ImageRGB8 foo