尝试掌握下面的Haskell type parameter
,
Prelude> data T a = C1 a | C2 (a -> a)
Prelude> :t C1 1
C1 1 :: Num a => T a
Prelude> :t C1 (+1)
C1 (+1) :: Num a => T (a -> a)
Prelude> :t C2 1
C2 1 :: Num (a -> a) => T a
Prelude> :t C2 (+1)
C2 (+1) :: Num a => T a
据我所知C1 1
是不可能的,C2 1
毫无意义,C1 (+1)
和C2 (+1)
似乎是矛盾的。
为什么上述类型检查都没有抱怨?非常感谢您的启发。
答案 0 :(得分:4)
在Haskell中,数字文字是多态的:
1 :: Int
1 :: Integer
1 :: Double
...
从技术上讲,这是通用类型
获得的1 :: Num a => a
所以," 1
可以是任何类型,前提是此类型属于Num
类"。
当您执行此操作时,例如,C2 1
,1
将被视为函数类型a -> a
,并生成额外约束以确保a -> a
为Num
:
C2 1 :: Num (a -> a) => T a
当然,功能不是数字。那为什么没有类型错误?因为在Haskell中,不禁止扩展Num
类以包含函数。你可以这样做,
例如:
instance Num b => Num (a -> b) where
fromInteger n = \_ -> n
x + y = \z -> x z + y z
...
有效地将1 :: a -> a
转变为"常数"功能
这可以启用诸如(设计示例如下)
之类的代码 case (some value of type T Int) of
C1 x -> x
C2 f -> (f + g) 50
-- assumning g :: Int -> Int
-- result is f 50 + g 50
如果您不提供实例,那么代码本身并不是错误的,因为稍后可能会添加实例。在这种情况下,约束将在您的代码中保留,因为它无法释放。如果您尝试将T a
转换为任何不涉及a
的类型,则需要释放Num
约束,此时编译器确实会抱怨。
可以通过注意到这是运算符部分来解释涉及(+1)
的案例,
所以它不仅仅是1前面的一元加号。特别是,
(+1) means \x -> x + 1
所以(+1)
实际上是继承函数。
答案 1 :(得分:1)
如果您希望出现错误,请创建一个情况,其中a
必须相同,例如:
> :t [C1 (+1), C2(+1)]
Couldn't match expected type ....
在您的示例a
中,a1
和a2
不同。没有错误