Is it possible to export constructors for pattern matching, but not for construction, in Haskell Modules?

前端 未结 3 464
予麋鹿
予麋鹿 2020-12-05 07:03

A vanilla data type in Haskell has zero or more constructors, each of which plays two roles.

In expressions, it supports introduction, its a function from zero or mo

相关标签:
3条回答
  • 2020-12-05 07:15

    You cannot. But if there are only reasonable number of constructors for your type T, you may want to hide the constructors and instead provide a function which does the pattern matching in the same spirit as maybe :: b -> (a -> b) -> Maybe a -> b.

    0 讨论(0)
  • 2020-12-05 07:24

    From GHC 7.8 on you can use PatternSynonyms to export patterns independent from constructors. So an analogue to @Lambdageek’s answer would be

    {-# LANGUAGE PatternSynonyms #-}
    
    module ThingModule (Thing, pattern Foo, pattern Bar) where
    
    pattern Foo a <- RealFoo a
    pattern Bar a <- RealBar a
    
    data Thing = RealFoo Thing | RealBar Int
    

    and

    {-# LANGUAGE PatternSynonyms #-}
    module Main where
    
    import ThingModule
    
    doSomethingWithThing :: Thing -> Int
    doSomethingWithThing (Foo x) = doSomethingWithThing x
    doSomethingWithThing (Bar y) = y
    

    So it looks like normal constructors.

    If you try to use Bar to construct a value, you get

    Main.hs:9:32:
        Bar used in an expression, but it's a non-bidirectional pattern synonym
        In the expression: Bar y
    
    0 讨论(0)
  • 2020-12-05 07:34

    You can use a view type and view patterns to do what you want:

    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
    

    Note that ThingView is not a recursive data type: all the value constructors refer back to Thing. So now you can export the value constructors of ThingView and keep Thing abstract.

    Use like this:

    {-# LANGUAGE ViewPatterns #-}
    module Main where
    
    import ThingModule
    
    doSomethingWithThing :: Thing -> Int
    doSomethingWithThing(view -> FooV x) = doSomethingWithThing x
    doSomethingWithThing(view -> BarV y) = y
    

    The arrow notation stuff is GHC's View Patterns. Note that it requires a language pragma.

    Of course you're not required to use view patterns, you can just do all the desugaring by hand:

    doSomethingWithThing :: Thing -> Int
    doSomethingWithThing = doIt . view
      where doIt (FooV x) = doSomethingWithThing x
            doIt (BarV y) = y
    

    More

    Actually we can do a little bit better: There is no reason to duplicate all the value constructors for both Thing and ThingView

    module ThingModule (ThingView(..), Thing, view) where
    
       newtype Thing = T {view :: ThingView Thing}
       data ThingView a = Foo a | Bar Int
    

    Continue useing it the same way as before, but now the pattern matches can use Foo and Bar.

    {-# LANGUAGE ViewPatterns #-}
    module Main where
    
    import ThingModule
    
    doSomethingWithThing :: Thing -> Int
    doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
    doSomethingWithThing(view -> Bar y) = y
    
    0 讨论(0)
提交回复
热议问题