gpt4 book ai didi

Haskell:为什么我不能在另一个函数中使用 id ,其中 id 的域显然是函数中所需类型的超集?

转载 作者:行者123 更新时间:2023-12-02 12:11:11 27 4
gpt4 key购买 nike

这里有一个简单的问题。我是 Haskell 的新手,正在使用 JuicyPixels 包处理一些图像。我已使用 decodePng 将图像加载到 GHCI 中的 DynamicImage 对象中。 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)
...

我想做的就是使用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
...

它需要一个从图像到任何东西的函数,即动态图像,并将该函数应用于基础数据。

为什么不

getImage :: Pixel a => DynamicImage -> Image a
getImage img = dynamicMap id img

类型检查?该错误似乎是因为 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

最佳答案

假设我们有 img::Image Pixel8 并运行

getImage (ImageY8 img) :: Image Pixel16

如何通过根本不操作位图的代码来神奇地转换它?这里肯定有什么问题。事实上,如果类型系统允许这样做,它将允许来自两种不同类型的危险转换,可能会导致崩溃。实际上,类型系统是健全的并且正确地拒绝了这种情况。

关键点是:

dynamicMap :: (forall pixel . (Pixel pixel) => Image pixel -> a)
-> DynamicImage -> a

这种类型是调用者和被调用者之间的契约。调用者可以选择a。然后调用者必须传递一个 forall Pixel 类型的函数参数。 (像素pixel)=>图像像素->a。这必须适用于所有像素类型。换句话说,被调用者 (dynamicMap) 可以选择 pixel。无法保证被调用者会选择 pixel 以满足 Image Pixel ~ a。事实上,它不会出现在发布的代码中。因此,编译器假设图像像素a可能不同。但是id强制它们相同:调用者施加了一个约束,限制了被调用者的选择。

因此出现类型错误。

更简单的情况:

foo :: (forall a. a -> Int) -> Int
foo f = f "hello" + f (42 :: Int) + f True

bar :: Int
bar = foo id

这里bar传递了一个函数id::Int -> Int,它不像forall a那么通用。 a->Int —— 后者 promise 将任何内容转换为 Int,由被调用者选择。因此,报告类型错误。

从技术上讲,id 的类型为 forall b。 b->b,我们的目标是forall a。 a->Int。无法替换类型 b(使用可能涉及类型变量 a 的类型),以便 b->b 变为 a->Int.

关于Haskell:为什么我不能在另一个函数中使用 id ,其中 id 的域显然是函数中所需类型的超集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38289439/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com