这是一个简单的问题.我是Haskell的新手,使用JuicyPixels包玩一些图像.我已将图像加载到DynamicImageGHCI中的对象中decodePng.DynamicImage类型只是包含几种不同像素类型的图像的包装:
data DynamicImage =
-- | A greyscale image.
ImageY8 (Image Pixel8)
-- | A greyscale image with 16bit components
| ImageY16 (Image Pixel16)
-- | A greyscale HDR image
| ImageYF (Image PixelF)
-- | An image in greyscale with an alpha channel.
| ImageYA8 (Image PixelYA8)
-- | An image in greyscale with alpha channel on 16 bits.
| ImageYA16 (Image PixelYA16)
...
Run Code Online (Sandbox Code Playgroud)
我想要做的就是使用底层数据dynamicMap并查看我正在加载的像素类型.dynamicMap的类型签名使用Rank2Types:
dynamicMap :: (forall pixel . (Pixel pixel) => Image pixel -> a)
-> DynamicImage -> a
dynamicMap f (ImageY8 i) = f i
dynamicMap f (ImageY16 i) = f i
dynamicMap f (ImageYF i) = f i
dynamicMap f (ImageYA8 i) = f i
...
Run Code Online (Sandbox Code Playgroud)
它需要一个从图像到任何东西的函数,一个dynamicImage,以及应用于底层数据的函数.
为什么不呢
getImage :: Pixel a => DynamicImage -> Image a
getImage img = dynamicMap id img
Run Code Online (Sandbox Code Playgroud)
类型检查?该错误似乎是因为该id函数在其输入中过于包容.
Couldn't match type `pixel' with `a'
`pixel' is a rigid type variable bound by
a type expected by the context:
Pixel pixel => Image pixel -> Image a
at <path>:24:16
`a' is a rigid type variable bound by
the type signature for
getImage :: Pixel a => DynamicImage -> Image a
at <path>:23:13
Expected type: Image pixel -> Image a
Actual type: Image a -> Image a
Relevant bindings include
getImage :: DynamicImage -> Image a
(bound at <path>:24:1)
In the first argument of `dynamicMap', namely `id'
In the expression: dynamicMap id img
Run Code Online (Sandbox Code Playgroud)
假设我们拥有img :: Image Pixel8并运行
getImage (ImageY8 img) :: Image Pixel16
Run Code Online (Sandbox Code Playgroud)
如何通过不操纵位图的代码进行神奇的转换?肯定有些事情是错的.实际上,如果类型系统允许这样做,它将允许来自两种不同类型的危险演员,可能导致崩溃.在实践中,类型系统是健全的并且正确地拒绝这一点.
关键点在于:
dynamicMap :: (forall pixel . (Pixel pixel) => Image pixel -> a)
-> DynamicImage -> a
Run Code Online (Sandbox Code Playgroud)
此类型是调用者和被调用者之间的契约.来电者可以选择a.然后调用者必须传递一个类型的函数参数forall pixel . (Pixel pixel) => Image pixel -> a.这必须适用于所有pixel类型.换句话说,callee(dynamicMap)可以选择pixel.无法保证被叫方会选择pixel这样来满足Image pixel ~ a.实际上,它不会在发布的代码中.因此,编译器假设Image pixel并且a可能不同.但id强迫它们是相同的:调用者施加限制,限制被调用者的选择.
因此类型错误.
一个更简单的案例:
foo :: (forall a. a -> Int) -> Int
foo f = f "hello" + f (42 :: Int) + f True
bar :: Int
bar = foo id
Run Code Online (Sandbox Code Playgroud)
这里bar传递一个函数id :: Int -> Int,它不像通常那样forall a. a->Int- 后者承诺将任何东西转换为Int被调用者的选择.因此,报告了类型错误.
从技术上讲,id有类型forall b. b->b,我们的目标forall a. a->Int.没有办法替换类型b(可能涉及类型变量的类型a),因此b->b变为a->Int.
| 归档时间: |
|
| 查看次数: |
100 次 |
| 最近记录: |