你可以使用 view type 和 view patterns 来做你想做的事:
module ThingModule (Thing, ThingView(..), view) where
data Thing = Foo Thing | Bar Int
data ThingView = FooV Thing | BarV Int
view :: Thing -> ThingView
view (Foo x) = FooV x
view (Bar y) = BarV y
请注意ThingView 不是递归数据类型:所有值构造函数都引用回Thing。所以现在你可以导出ThingView 的值构造函数并保持Thing 抽象。
这样使用:
{-# LANGUAGE ViewPatterns #-}
module Main where
import ThingModule
doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> FooV x) = doSomethingWithThing x
doSomethingWithThing(view -> BarV y) = y
箭头符号是 GHC 的View Patterns。请注意,它需要语言编译指示。
当然你不需要使用视图模式,你可以手动完成所有的脱糖:
doSomethingWithThing :: Thing -> Int
doSomethingWithThing = doIt . view
where doIt (FooV x) = doSomethingWithThing x
doIt (BarV y) = y
更多
实际上我们可以做得更好一点:没有理由复制Thing 和ThingView 的所有值构造函数
module ThingModule (ThingView(..), Thing, view) where
newtype Thing = T {view :: ThingView Thing}
data ThingView a = Foo a | Bar Int
继续像以前一样使用它,但现在模式匹配可以使用Foo 和Bar。
{-# LANGUAGE ViewPatterns #-}
module Main where
import ThingModule
doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
doSomethingWithThing(view -> Bar y) = y