在haskell中编写FFI代码时,我经常将Int
和CInt
个变量混合在一起。我尝试定义一个新类型Intlike
来帮助定义可以表示为任一类型值的常量,如下所示:
type Intlike = forall a . (Num a, Integral a) => a
floatSize :: Intlike = fromIntegral $ sizeOf (1 :: CFloat)
然后GHCi抱怨:
Fractal.hs:276:24-35: No instance for (Num Intlike) arising from \
a use of ‘fromIntegral’ …
In the expression: fromIntegral
In the expression: fromIntegral $ sizeOf (1 :: CFloat)
In a pattern binding:
floatSize :: Intlike = fromIntegral $ sizeOf (1 :: CFloat)
Compilation failed.
(这是Rank2Types
语言扩展名。)
然而,以下内容有效:
floatSize :: (Num a, Integral a) => a
floatSize = fromIntegral $ sizeOf (1 :: CFloat)
有没有一个好的解决方案,我不会一直写fromIntegral
? Intlike
和有效的有什么区别?它们看起来很相似。
答案 0 :(得分:2)
我无法告诉你为什么,但如果你写
floatSize :: Intlike
floatSize = fromIntegral $ sizeOf (1 :: CFloat)
然后它工作得很好。一种可能性是模式变量上的类型注释正在做一些不同于你预期的事情(我从未真正理解它们的作用)。请注意,您的Num
上下文是多余的,因为Integral
是Num
的子类。对于fromIntegral
,您几乎需要在整数类型之间切换。另一个选择是使用" generic"能够满足您需求的功能。例如,您可以定义
import Foreign.Storable (sizeOf, Storable)
genericSizeOf :: (Storable a, Integral b) => a -> b
genericSizeOf = fromIntegral . sizeOf
旁注:当使用像sizeOf
这样仅仅为其类型提取参数的函数时,我个人更喜欢使用undefined
而不是任意值。所以我写的是sizeOf (undefined::CInt)
而不是sizeof (1::CInt)
。这清楚地表明,我传入的价值并不重要,并且减少了#34的心理混乱。这是什么?如果我把它改成2会怎么样?"
在下面的评论中,Boyd Stephen Smith,Jr。提到了另一种显然更受欢迎的方法,尽管我还没有读过它。