最佳实践 #

一、代码组织 #

1.1 模块结构 #

haskell
-- 标准模块结构
module MyLib.ModuleName (
    -- 类型导出
    Type(..),
    
    -- 函数导出
    function1,
    function2,
    
    -- 重导出
    module Data.Maybe
) where

import Data.Maybe

-- 类型定义
data Type = ...

-- 函数定义
function1 = ...

1.2 导入组织 #

haskell
module MyModule where

-- 标准库
import Data.List (sort, nub)
import Data.Maybe (fromMaybe)
import Control.Monad (forM_, when)

-- 第三方库
import qualified Data.Text as T
import qualified Data.Map as M

-- 本地模块
import MyLib.Types
import MyLib.Utils

1.3 项目结构 #

text
my-project/
├── app/              # 可执行文件
│   └── Main.hs
├── src/              # 库代码
│   └── MyLib/
│       ├── Types.hs
│       ├── Core.hs
│       └── Utils.hs
├── test/             # 测试
│   └── MyLib/
│       └── CoreSpec.hs
├── benchmark/        # 性能测试
│   └── Bench.hs
├── README.md
├── ChangeLog.md
├── LICENSE
├── package.yaml
└── stack.yaml

二、代码风格 #

2.1 类型签名 #

haskell
-- 始终添加类型签名
function :: Int -> String -> Bool
function n s = n > length s

-- 类型类约束放在签名中
process :: (Show a, Eq a) => a -> String
process x = show x

2.2 函数定义 #

haskell
-- 简单函数:单行
add :: Int -> Int -> Int
add x y = x + y

-- 复杂函数:使用where或let
complexCalc :: Int -> Int
complexCalc x = result
  where
    step1 = x * 2
    step2 = step1 + 1
    result = step2 ^ 2

2.3 模式匹配 #

haskell
-- 使用模式匹配而非if-then-else
describe :: Int -> String
describe 0 = "zero"
describe 1 = "one"
describe n
    | n < 0     = "negative"
    | otherwise = "many"

-- 使用模式匹配解构
process :: (Int, String) -> String
process (n, s) = s ++ ": " ++ show n

2.4 使用类型提高安全性 #

haskell
-- 使用newtype区分类型
newtype UserId = UserId Int
newtype ProductId = ProductId Int

-- 而非
-- type UserId = Int
-- type ProductId = Int

三、性能优化 #

3.1 严格求值 #

haskell
import Data.List (foldl')

-- 使用foldl'而非foldl
sum' :: [Int] -> Int
sum' = foldl' (+) 0

-- 使用严格数据类型
data Config = Config
    { configHost :: !String
    , configPort :: !Int
    }

3.2 避免空间泄漏 #

haskell
-- 不好:构建大量thunk
badSum :: [Int] -> Int
badSum [] = 0
badSum (x:xs) = x + badSum xs

-- 好:使用严格累加器
goodSum :: [Int] -> Int
goodSum = go 0
  where
    go !acc [] = acc
    go !acc (x:xs) = go (acc + x) xs

3.3 选择正确的数据结构 #

haskell
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Vector as V

-- 频繁查找:使用Map
lookupTable :: M.Map String Int

-- 唯一元素:使用Set
uniqueItems :: S.Set Int

-- 随机访问:使用Vector
randomAccess :: V.Vector Double

3.4 内联关键函数 #

haskell
-- 使用INLINE编译指示
{-# INLINE criticalFunction #-}
criticalFunction :: Int -> Int
criticalFunction = (*2)

四、错误处理 #

4.1 使用Maybe和Either #

haskell
-- 使用Maybe处理可能失败的操作
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x

-- 使用Either提供错误信息
safeDiv :: Int -> Int -> Either String Int
safeDiv _ 0 = Left "Division by zero"
safeDiv x y = Right (x `div` y)

4.2 避免部分函数 #

haskell
-- 不好:部分函数可能崩溃
head :: [a] -> a
tail :: [a] -> [a]

-- 好:使用安全版本
safeHead :: [a] -> Maybe a
safeTail :: [a] -> Maybe [a]

4.3 自定义错误类型 #

haskell
data AppError
    = NetworkError String
    | ParseError String
    | ValidationError String
    deriving (Show)

handleError :: AppError -> IO ()
handleError (NetworkError msg) = putStrLn $ "Network: " ++ msg
handleError (ParseError msg) = putStrLn $ "Parse: " ++ msg
handleError (ValidationError msg) = putStrLn $ "Validation: " ++ msg

五、测试 #

5.1 单元测试 #

haskell
-- test/MyLib/CoreSpec.hs
module MyLib.CoreSpec (spec) where

import Test.Hspec
import MyLib.Core

spec :: Spec
spec = do
    describe "add" $ do
        it "adds two numbers" $ do
            add 1 2 `shouldBe` 3
        
        it "handles zero" $ do
            add 0 0 `shouldBe` 0

5.2 属性测试 #

haskell
import Test.QuickCheck

prop_addCommutative :: Int -> Int -> Bool
prop_addCommutative x y = add x y == add y x

prop_addIdentity :: Int -> Bool
prop_addIdentity x = add x 0 == x

5.3 运行测试 #

bash
# 使用HSpec
cabal test

# 使用QuickCheck
quickCheck prop_addCommutative

六、调试 #

6.1 Debug.Trace #

haskell
import Debug.Trace

-- 添加调试输出
debugFunction :: Int -> Int
debugFunction x = trace ("input: " ++ show x) (x * 2)

-- 带标签的调试
debugFunction' :: Int -> Int
debugFunction' x = traceShowId x * 2

6.2 GHCi调试 #

haskell
-- 在GHCi中
:break function  -- 设置断点
:step            -- 单步执行
:continue        -- 继续
:print var       -- 打印变量

6.3 类型错误调试 #

haskell
-- 使用TypedHoles
function :: Int -> String
function x = _  -- 编译器会提示类型

-- 使用TypeApplications
show @Int 42  -- 显式指定类型

七、文档 #

7.1 Haddock注释 #

haskell
-- | 计算两个数的和
--
-- >>> add 1 2
-- 3
add :: Int -> Int -> Int
add x y = x + y

-- | 安全除法
--
-- 返回 'Nothing' 如果除数为零
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

7.2 模块文档 #

haskell
-- |
-- Module      : MyLib.Core
-- Description : 核心功能
-- Copyright   : (c) Author, 2024
-- License     : BSD-3-Clause
--
-- 核心功能模块
module MyLib.Core (
    -- * 基本操作
    add,
    subtract,
    
    -- * 高级操作
    multiply,
    divide
) where

八、工具 #

8.1 代码格式化 #

bash
# 使用ormolu
ormolu --mode inplace src/**/*.hs

# 使用fourmolu
fourmolu --mode inplace src/**/*.hs

8.2 代码检查 #

bash
# 使用hlint
hlint src/

# 使用weeder
weeder .

8.3 性能分析 #

bash
# 编译时启用分析
cabal build --enable-profiling

# 运行分析
cabal run my-project +RTS -p

九、总结 #

最佳实践要点:

  1. 代码组织:清晰的模块结构
  2. 代码风格:类型签名、模式匹配
  3. 性能优化:严格求值、正确数据结构
  4. 错误处理:使用Maybe和Either
  5. 测试:单元测试、属性测试
  6. 调试:Debug.Trace、GHCi
  7. 文档:Haddock注释
  8. 工具:格式化、检查、分析

恭喜你完成了Haskell语言完全指南的学习!

最后更新:2026-03-27