Вы можете использовать тип представления и шаблоны просмотра , чтобы делать то, что вы хотите:
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
abstract.
Использовать так:
{-# 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