类型别名 #
一、type关键字 #
1.1 什么是类型别名 #
类型别名是现有类型的替代名称:
haskell
-- 类型别名
type String = [Char]
-- 使用
greeting :: String
greeting = "Hello"
1.2 基本语法 #
haskell
-- 语法
type 别名 = 现有类型
-- 示例
type Name = String
type Age = Int
type Point = (Double, Double)
1.3 使用场景 #
haskell
-- 提高可读性
type UserId = Int
type UserName = String
getUser :: UserId -> UserName
getUser uid = "User" ++ show uid
-- 简化复杂类型
type StringMap = [(String, String)]
type IntList = [Int]
二、参数化类型别名 #
2.1 带参数的类型别名 #
haskell
-- 类型参数
type Pair a = (a, a)
-- 使用
intPair :: Pair Int
intPair = (1, 2)
stringPair :: Pair String
stringPair = ("hello", "world")
2.2 多参数类型别名 #
haskell
-- 多个参数
type Map k v = [(k, v)]
-- 使用
intStringMap :: Map Int String
intStringMap = [(1, "one"), (2, "two")]
-- 部分应用
type StringMap = Map String
-- StringMap v = [(String, v)]
2.3 常用类型别名 #
haskell
-- 常用别名
type Predicate a = a -> Bool
type Transformer a b = a -> b
type Comparator a = a -> a -> Ordering
-- 使用
isEven :: Predicate Int
isEven = even
double :: Transformer Int Int
double = (*2)
compareLength :: Comparator String
compareLength s1 s2 = compare (length s1) (length s2)
三、类型别名与文档 #
3.1 语义化类型 #
haskell
-- 语义化命名
type Seconds = Int
type Pixels = Int
type Percentage = Double
-- 使用
delay :: Seconds -> IO ()
delay secs = putStrLn $ "Waiting " ++ show secs ++ " seconds"
resize :: Pixels -> Pixels -> Image -> Image
resize width height img = ...
discount :: Percentage -> Price -> Price
discount pct price = price * (1 - pct / 100)
3.2 复杂类型简化 #
haskell
-- 简化复杂类型签名
type Handler = Request -> Response
type Middleware = Handler -> Handler
type Router = [Middleware] -> Handler
-- 不使用别名
-- app :: [([Request] -> Response) -> Request -> Response] -> [Request] -> Response -> Response
-- 使用别名
app :: [Middleware] -> Handler
app = ...
四、类型别名的限制 #
4.1 不创建新类型 #
haskell
-- 类型别名只是别名,不创建新类型
type Age = Int
type Year = Int
-- 可以混用
age :: Age
age = 25
year :: Year
year = 2024
-- 错误不会被检测
wrong = age + year -- 编译通过,但语义错误
4.2 不能用于实例 #
haskell
-- 不能为类型别名定义实例
type MyInt = Int
-- 错误!
-- instance Show MyInt where ...
4.3 不能递归 #
haskell
-- 类型别名不能递归
-- type BadList a = a | Cons a (BadList a) -- 错误!
五、newtype #
5.1 什么是newtype #
newtype创建一个新类型,但运行时表示与原类型相同:
haskell
-- newtype语法
newtype Age = Age Int
-- 使用
myAge :: Age
myAge = Age 25
-- 解包
getAge :: Age -> Int
getAge (Age n) = n
5.2 newtype vs type #
haskell
-- type:只是别名
type AgeType = Int
-- AgeType和Int完全相同
-- newtype:创建新类型
newtype AgeNew = AgeNew Int
-- AgeNew和Int是不同类型
5.3 newtype vs data #
haskell
-- data:可以有多个构造器
data AgeData = AgeData Int | UnknownAge
-- newtype:只有一个构造器,一个字段
newtype AgeNew = AgeNew Int
-- newtype没有运行时开销
-- data有构造器标记
六、newtype应用 #
6.1 类型安全 #
haskell
-- 使用newtype区分类型
newtype UserId = UserId Int
newtype ProductId = ProductId Int
-- 不能混用
-- userId :: UserId
-- userId = ProductId 1 -- 编译错误!
-- 需要显式转换
toUserId :: Int -> UserId
toUserId = UserId
fromUserId :: UserId -> Int
fromUserId (UserId n) = n
6.2 派生实例 #
haskell
-- 派生类型类
newtype Age = Age Int
deriving (Show, Eq, Ord, Num)
-- 使用
age1 = Age 25
age2 = Age 30
age1 + age2 -- Age 55
age1 < age2 -- True
6.3 包装函数类型 #
haskell
-- 包装函数类型
newtype Predicate a = Predicate (a -> Bool)
-- 使用
isEven :: Predicate Int
isEven = Predicate even
runPredicate :: Predicate a -> a -> Bool
runPredicate (Predicate p) x = p x
-- 组合谓词
andP :: Predicate a -> Predicate a -> Predicate a
andP (Predicate p1) (Predicate p2) = Predicate (\x -> p1 x && p2 x)
6.4 Monoid实例 #
haskell
-- Sum和Product
newtype Sum a = Sum { getSum :: a }
newtype Product a = Product { getProduct :: a }
-- Num a => Monoid (Sum a)
instance Num a => Semigroup (Sum a) where
Sum x <> Sum y = Sum (x + y)
instance Num a => Monoid (Sum a) where
mempty = Sum 0
-- 使用
sumAll = getSum $ mconcat [Sum 1, Sum 2, Sum 3] -- 6
七、实践示例 #
7.1 单位类型 #
haskell
-- 带单位的数值
newtype Seconds = Seconds Double
newtype Pixels = Pixels Int
newtype Dollars = Dollars Double
-- 安全操作
addSeconds :: Seconds -> Seconds -> Seconds
addSeconds (Seconds a) (Seconds b) = Seconds (a + b)
-- 需要显式转换
secondsToMinutes :: Seconds -> Double
secondsToMinutes (Seconds s) = s / 60
7.2 ID类型 #
haskell
-- 不同实体的ID
newtype UserId = UserId Int deriving (Show, Eq)
newtype ProductId = ProductId Int deriving (Show, Eq)
newtype OrderId = OrderId String deriving (Show, Eq)
-- 不能混用
getUser :: UserId -> User
getUser (UserId n) = ...
getProduct :: ProductId -> Product
getProduct (ProductId n) = ...
7.3 配置类型 #
haskell
-- 配置类型别名
type Host = String
type Port = Int
type Timeout = Int
data Config = Config
{ configHost :: Host
, configPort :: Port
, configTimeout :: Timeout
}
-- 使用
defaultConfig :: Config
defaultConfig = Config
{ configHost = "localhost"
, configPort = 8080
, configTimeout = 30
}
7.4 解析器类型 #
haskell
-- 解析器类型
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
-- 基本解析器
charP :: Char -> Parser Char
charP c = Parser $ \s ->
case s of
(x:xs) | x == c -> Just (c, xs)
_ -> Nothing
-- Functor实例
instance Functor Parser where
fmap f (Parser p) = Parser $ \s ->
fmap (\(x, s') -> (f x, s')) (p s)
-- Applicative实例
instance Applicative Parser where
pure x = Parser $ \s -> Just (x, s)
Parser pf <*> Parser px = Parser $ \s ->
case pf s of
Nothing -> Nothing
Just (f, s') -> fmap (\(x, s'') -> (f x, s'')) (px s')
八、类型别名最佳实践 #
8.1 何时使用type #
haskell
-- 好:简化复杂类型
type Handler = Request -> Response
-- 好:语义化命名
type Name = String
-- 不好:需要类型安全
-- type Age = Int -- 不安全,使用newtype
8.2 何时使用newtype #
haskell
-- 好:需要类型安全
newtype Age = Age Int
-- 好:定义类型类实例
newtype Sum a = Sum a
-- 好:包装函数类型
newtype Predicate a = Predicate (a -> Bool)
8.3 命名约定 #
haskell
-- 类型别名:使用简单名词
type Name = String
type Point = (Double, Double)
-- newtype:使用大写开头
newtype Age = Age Int
newtype UserId = UserId Int
九、总结 #
类型别名要点:
- type:创建类型别名,不创建新类型
- 参数化:类型别名可以有类型参数
- 用途:简化复杂类型、语义化命名
- 限制:不创建新类型、不能定义实例
- newtype:创建新类型,零运行时开销
- newtype用途:类型安全、定义实例、包装函数
掌握类型别名后,让我们继续学习类型类基础。
最后更新:2026-03-27