Haskell:类型变量和泛型类型
Haskell: type variables and generic types
当我在函数的类型签名中明确提到 isQuestion
类型时,GHCi 完美地编译了它:
isQuestion :: [Char] -> Maybe Bool
isQuestion [] = Nothing
isQuestion xs = Just (last xs == '?')
但是,当我转向 'generic' 代码时,它不起作用:
isQuestion :: [a] -> Maybe b
isQuestion [] = Nothing
isQuestion xs = Just (last xs == '?')
因为我得到以下错误:
<interactive>:138:17: error:
* Couldn't match type `b' with `Bool'
`b' is a rigid type variable bound by
the type signature for:
isQuestion :: forall a b. [a] -> Maybe b
at <interactive>:136:1-28
Expected type: Maybe b
Actual type: Maybe Bool
* In the expression: Just (last xs == '?')
In an equation for `isQuestion':
isQuestion xs = Just (last xs == '?')
* Relevant bindings include
isQuestion :: [a] -> Maybe b (bound at <interactive>:137:1)
对于 forall a b. [a] -> Maybe b
,类型 [a] -> Maybe b
是 shorthand。但是 isQuestion
不适用于 所有 类型 a
和 b
,它仅在 a
是 [=16= 时才有效] 并且 b
是 Bool
.
参数多态性 不是 子类型或超类型。它声明值的实现不依赖于部分类型。事实上,该实现适用于任何类型的选择。
您的实施不适用于任何类型选择。它仅在 a
为 Char
且 b
为 Bool
时有效。你可以看出这一点,因为它在那里使用了 (== '?')
,这是一个 Char -> Bool
.
类型的函数
第一个观察结果是
last xs == something
只有在 xs
的元素范围内有 (==)
的定义时才有效。但是由于编译器对 a
一无所知,所以没有这样的东西。您必须将 a
缩小为具有相等性的类型的子集:
isQuestion :: Eq a => [a] -> Maybe b
第二个观察结果是 something
在您的代码 ('?'
) 中是一个 Char
,因此此方法只能在 a ≡ Char
时起作用。相反,您可以将其添加为参数:
isQuestion :: Eq a => a -> [a] -> Maybe b
最后,如前所述,您有一个具体的 return 类型,即 Maybe Bool
,如
(==) :: a -> a -> Bool
所以你的函数的签名可以是
isQuestion :: Eq a => a -> [a] -> Maybe Bool
为清楚起见编辑了这一段请注意,根据您的业务逻辑,您可能不想对空字符串进行特殊处理。如果您唯一关心的是字符串是否以问号结尾,那么 Nothing
和 Just false
实际上是同一个意思。在这种情况下,你的函数变成了一个 "yes or no" 问题,你可以删除 Maybe
:
isQuestion :: Eq a => a -> [a] -> Bool
isQuestion x = isSuffixOf [x]
或者干脆
isQuestion :: String -> Bool
isQuestion = isSuffixOf "?"
当我在函数的类型签名中明确提到 isQuestion
类型时,GHCi 完美地编译了它:
isQuestion :: [Char] -> Maybe Bool
isQuestion [] = Nothing
isQuestion xs = Just (last xs == '?')
但是,当我转向 'generic' 代码时,它不起作用:
isQuestion :: [a] -> Maybe b
isQuestion [] = Nothing
isQuestion xs = Just (last xs == '?')
因为我得到以下错误:
<interactive>:138:17: error:
* Couldn't match type `b' with `Bool'
`b' is a rigid type variable bound by
the type signature for:
isQuestion :: forall a b. [a] -> Maybe b
at <interactive>:136:1-28
Expected type: Maybe b
Actual type: Maybe Bool
* In the expression: Just (last xs == '?')
In an equation for `isQuestion':
isQuestion xs = Just (last xs == '?')
* Relevant bindings include
isQuestion :: [a] -> Maybe b (bound at <interactive>:137:1)
对于 forall a b. [a] -> Maybe b
,类型 [a] -> Maybe b
是 shorthand。但是 isQuestion
不适用于 所有 类型 a
和 b
,它仅在 a
是 [=16= 时才有效] 并且 b
是 Bool
.
参数多态性 不是 子类型或超类型。它声明值的实现不依赖于部分类型。事实上,该实现适用于任何类型的选择。
您的实施不适用于任何类型选择。它仅在 a
为 Char
且 b
为 Bool
时有效。你可以看出这一点,因为它在那里使用了 (== '?')
,这是一个 Char -> Bool
.
第一个观察结果是
last xs == something
只有在 xs
的元素范围内有 (==)
的定义时才有效。但是由于编译器对 a
一无所知,所以没有这样的东西。您必须将 a
缩小为具有相等性的类型的子集:
isQuestion :: Eq a => [a] -> Maybe b
第二个观察结果是 something
在您的代码 ('?'
) 中是一个 Char
,因此此方法只能在 a ≡ Char
时起作用。相反,您可以将其添加为参数:
isQuestion :: Eq a => a -> [a] -> Maybe b
最后,如前所述,您有一个具体的 return 类型,即 Maybe Bool
,如
(==) :: a -> a -> Bool
所以你的函数的签名可以是
isQuestion :: Eq a => a -> [a] -> Maybe Bool
为清楚起见编辑了这一段请注意,根据您的业务逻辑,您可能不想对空字符串进行特殊处理。如果您唯一关心的是字符串是否以问号结尾,那么 Nothing
和 Just false
实际上是同一个意思。在这种情况下,你的函数变成了一个 "yes or no" 问题,你可以删除 Maybe
:
isQuestion :: Eq a => a -> [a] -> Bool
isQuestion x = isSuffixOf [x]
或者干脆
isQuestion :: String -> Bool
isQuestion = isSuffixOf "?"