类型签名 #
一、类型签名基础 #
1.1 基本语法 #
类型签名使用 :: 操作符:
haskell
-- 变量类型签名
name :: String
name = "Haskell"
-- 函数类型签名
add :: Int -> Int -> Int
add x y = x + y
-- 多参数函数
multiply :: Int -> Int -> Int -> Int
multiply x y z = x * y * z
1.2 类型签名位置 #
haskell
-- 类型签名通常放在定义之前
double :: Int -> Int
double x = x * 2
-- 也可以在表达式级别添加类型
result = (5 :: Int) + (3 :: Int)
-- GHCi中使用:t查看类型
-- :t double
-- double :: Int -> Int
1.3 函数类型 #
haskell
-- 单参数函数
square :: Int -> Int
square x = x * x
-- 多参数函数
-- 箭头连接参数和返回类型
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
-- 理解为:Int -> (Int -> (Int -> Int))
-- 柯里化形式
二、类型约束 #
2.1 类型类约束 #
haskell
-- Eq约束
isEqual :: Eq a => a -> a -> Bool
isEqual x y = x == y
-- Ord约束
isGreater :: Ord a => a -> a -> Bool
isGreater x y = x > y
-- Num约束
double :: Num a => a -> a
double x = x * 2
2.2 多重约束 #
haskell
-- 多个约束用括号包围
showEqual :: (Eq a, Show a) => a -> a -> String
showEqual x y =
if x == y
then show x ++ " equals " ++ show y
else show x ++ " not equals " ++ show y
-- Num和Show约束
doubleAndShow :: (Num a, Show a) => a -> String
doubleAndShow x = show (x * 2)
2.3 约束的作用 #
haskell
-- 约束限制了可用的操作
constrained :: Num a => a -> a
constrained x = x + 1 -- 可以使用+操作
-- 无约束的函数
identity :: a -> a
identity x = x -- 不能对x做任何操作
三、类型推断 #
3.1 自动推断 #
Haskell编译器可以自动推断类型:
haskell
-- 无需类型签名
add x y = x + y
-- 推断为:Num a => a -> a -> a
multiply x y = x * y
-- 推断为:Num a => a -> a -> a
isEqual x y = x == y
-- 推断为:Eq a => a -> a -> Bool
3.2 推断规则 #
haskell
-- 根据使用的操作推断
usePlus x y = x + y
-- 使用了+,推断为Num
useCompare x y = x < y
-- 使用了<,推断为Ord
useEqual x y = x == y
-- 使用了==,推断为Eq
useShow x = show x
-- 使用了show,推断为Show
3.3 类型默认 #
当类型模糊时,使用默认类型:
haskell
-- 默认规则
number = 42 -- 默认为Integer
decimal = 3.14 -- 默认为Double
char = 'a' -- Char
string = "hello" -- [Char]
3.4 类型歧义 #
haskell
-- 类型歧义错误
-- ambiguous = show 42
-- 错误:不知道show返回什么类型
-- 解决方法1:添加类型签名
ambiguous :: String
ambiguous = show 42
-- 解决方法2:使用类型应用
ambiguous' = show @Int 42
四、函数类型详解 #
4.1 箭头语义 #
haskell
-- 多参数函数类型
func :: a -> b -> c -> d
-- 等价于
func :: a -> (b -> (c -> d))
-- 这意味着函数是柯里化的
-- 每次只接受一个参数
4.2 返回函数 #
haskell
-- 返回函数的函数
makeAdder :: Int -> (Int -> Int)
makeAdder x = (\y -> x + y)
-- 使用
addFive :: Int -> Int
addFive = makeAdder 5
result = addFive 10 -- 15
4.3 高阶函数类型 #
haskell
-- map的类型
map :: (a -> b) -> [a] -> [b]
-- filter的类型
filter :: (a -> Bool) -> [a] -> [a]
-- foldl的类型
foldl :: (b -> a -> b) -> b -> [a] -> b
-- 函数组合的类型
(.) :: (b -> c) -> (a -> b) -> (a -> c)
五、多态类型 #
5.1 参数多态 #
haskell
-- 完全多态
identity :: a -> a
identity x = x
-- 可以用于任何类型
-- identity 5 :: Int
-- identity "hello" :: String
-- identity True :: Bool
5.2 受限多态 #
haskell
-- 类型类约束的多态
showIt :: Show a => a -> String
showIt x = show x
-- 只能用于Show类型类的实例
-- showIt 5 :: String
-- showIt "hello" :: String
-- showIt True :: String
5.3 多类型参数 #
haskell
-- 多个类型变量
pair :: a -> b -> (a, b)
pair x y = (x, y)
-- 类型变量可以相同
samePair :: a -> (a, a)
samePair x = (x, x)
六、类型别名 #
6.1 type关键字 #
haskell
-- 类型别名
type Name = String
type Age = Int
-- 使用类型别名
person :: Name -> Age -> String
person name age = name ++ " is " ++ show age
-- 类型别名只是别名,不创建新类型
-- Name和String完全等价
6.2 参数化类型别名 #
haskell
-- 带参数的类型别名
type Pair a = (a, a)
type StringMap = [(String, String)]
-- 使用
makePair :: a -> Pair a
makePair x = (x, x)
6.3 类型别名与文档 #
haskell
-- 使用类型别名提高可读性
type UserId = Int
type UserName = String
type UserEmail = String
getUser :: UserId -> (UserName, UserEmail)
getUser uid = ("John", "john@example.com")
七、类型应用 #
7.1 可见类型应用 #
haskell
{-# LANGUAGE TypeApplications #-}
-- 显式指定类型参数
readInt :: String -> Int
readInt = read @Int
-- 使用
value = read @Int "42"
-- 多类型参数
pair :: a -> b -> (a, b)
pair = (,)
intString = pair @Int @String 42 "hello"
7.2 部分类型应用 #
haskell
{-# LANGUAGE TypeApplications #-}
-- 部分应用类型
type StringPair = (,) String
-- 等价于
-- type StringPair a = (String, a)
八、类型签名风格 #
8.1 单行风格 #
haskell
-- 简单函数
add :: Int -> Int -> Int
add x y = x + y
-- 带约束
showAdd :: (Num a, Show a) => a -> a -> String
showAdd x y = show (x + y)
8.2 多行风格 #
haskell
-- 复杂类型
transform :: (Functor f, Monad m)
=> (a -> b)
-> f a
-> m (f b)
transform f fa = return (fmap f fa)
-- 长参数列表
function :: Int
-> String
-> Bool
-> [Double]
-> Maybe Int
function = ...
8.3 行内类型 #
haskell
-- 在表达式中添加类型
result = sum (map (+(1 :: Int)) [1..10])
-- 使用Case表达式
process x = case x of
(n :: Int) -> n * 2
九、实践示例 #
9.1 常用函数类型 #
haskell
-- 列表函数
head :: [a] -> a
tail :: [a] -> [a]
length :: [a] -> Int
null :: [a] -> Bool
-- Maybe函数
fromMaybe :: a -> Maybe a -> a
maybe :: b -> (a -> b) -> Maybe a -> b
-- Either函数
either :: (a -> c) -> (b -> c) -> Either a b -> c
9.2 自定义函数类型 #
haskell
-- 安全除法
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)
-- 查找函数
find :: (a -> Bool) -> [a] -> Maybe a
find _ [] = Nothing
find p (x:xs)
| p x = Just x
| otherwise = find p xs
-- 组合函数
compose :: (b -> c) -> (a -> b) -> (a -> c)
compose f g x = f (g x)
十、总结 #
类型签名要点:
- 基本语法:使用
::添加类型签名 - 函数类型:箭头连接参数和返回类型
- 类型约束:使用
=>添加类型类约束 - 类型推断:编译器可以自动推断类型
- 多态类型:使用类型变量实现多态
- 类型别名:使用
type创建类型别名
掌握类型签名后,让我们继续学习类型变量。
最后更新:2026-03-27