它编译是因为type
没有引入新的不同类型:
类型同义词声明引入了与旧类型等效的新类型。[...]
类型同义词是一种方便但严格语法的机制,可以使类型签名更具可读性。同义词及其定义是完全可以互换的, [...]
完全没有限制,在任何级别,包括类型签名。您可以通过一个更短的示例看到这一点:
type Clown a b = Double
proof :: Clown a b -> Clown b a
proof = id
因为Clown a b
和Clown b a
都是——不管Double
实际的a
和b
——都可以交换为Double
, 和proof
的类型是Double -> Double
。
虽然您的约束限制了a
in的可能类型Length a
,但它实际上并没有改变结果类型的语义。相反,使用newtype
:
data LengthUnit = Km | Meter
newtype Length (unit::LengthUnit) = MkLength {getLength :: Double}
onLength :: (Double -> Double) -> Length a -> Length a
onLength f = MkLength . f . getLength
divideByTwo ::Length l -> Length l
divideByTwo = onLength (/ 2)
getKm ::Double -> Length Km
getKm = MkLength
-- other code omitted
现在您将获得所需的编译术语错误,因为Length Km
和Length Meter
是不同的类型:
test.hs:25:44:
Couldn't match type 'Meter with 'Km
Expected type: Length 'Km
Actual type: Length 'Meter
In the second argument of `($)', namely `divideByTwo $ getMeter 1'
In the expression: isKm $ divideByTwo $ getMeter 1
In an equation for `this_should_not_compile_but_it_does':
this_should_not_compile_but_it_does
= isKm $ divideByTwo $ getMeter 1
test.hs:27:53:
Couldn't match type 'Meter with 'Km
Expected type: Length 'Km
Actual type: Length 'Meter
In the second argument of `($)', namely `getMeter 1'
In the expression: isKm $ getMeter 1