最佳实践 #
一、代码组织 #
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
九、总结 #
最佳实践要点:
- 代码组织:清晰的模块结构
- 代码风格:类型签名、模式匹配
- 性能优化:严格求值、正确数据结构
- 错误处理:使用Maybe和Either
- 测试:单元测试、属性测试
- 调试:Debug.Trace、GHCi
- 文档:Haddock注释
- 工具:格式化、检查、分析
恭喜你完成了Haskell语言完全指南的学习!
最后更新:2026-03-27