类型别名 #

一、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

九、总结 #

类型别名要点:

  1. type:创建类型别名,不创建新类型
  2. 参数化:类型别名可以有类型参数
  3. 用途:简化复杂类型、语义化命名
  4. 限制:不创建新类型、不能定义实例
  5. newtype:创建新类型,零运行时开销
  6. newtype用途:类型安全、定义实例、包装函数

掌握类型别名后,让我们继续学习类型类基础。

最后更新:2026-03-27