【问题标题】:Is it possible to export constructors for pattern matching, but not for construction, in Haskell Modules?是否可以在 Haskell 模块中导出用于模式匹配的构造函数,但不能用于构造?
【发布时间】:2014-08-06 21:31:47
【问题描述】:

Haskell 中的普通数据类型有零个或多个构造函数,每个构造函数扮演两个角色。

在表达式中,它支持引入,它是一个从零个或多个参数到数据类型的函数。

在模式中,它支持消除,有点像从数据类型到 Maybe(参数类型的元组)的函数。

模块签名是否可以隐藏前者而暴露后者?

用例是这样的:我有一个类型,T,它的构造函数类型有时可以用来构造废话。我有构造函数,可用于构建保证不是废话的类型的实例。在这种情况下隐藏构造函数是有意义的,但是对于调用者来说,能够对他们使用构造函数构建的有保证的非废话进行模式匹配仍然很有用。

我怀疑这是不可能的,但如果有人有办法做到这一点,我想问问。

下一个最好的办法是隐藏构造函数并从 T -> Maybe (This, That), T -> Maybe (The, Other, Thing) 等创建一堆函数。

【问题讨论】:

    标签: haskell pattern-matching


    【解决方案1】:

    你可以使用 view typeview 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
    

    更多

    实际上我们可以做得更好一点:没有理由复制ThingThingView 的所有值构造函数

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

    继续像以前一样使用它,但现在模式匹配可以使用FooBar

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

    【讨论】:

    • 这太棒了!尤其是那种新型。一个问题是 ghc -Wall 会抱怨 doSomethingWithThing 具有非详尽的模式匹配。不知道为什么?
    • @Joey 这是使用视图模式时模式覆盖检查的已知错误。在某些版本的 GHC 上,当使用视图模式时,您总是会收到该警告。不确定它是否已在更新的版本中得到修复。
    【解决方案2】:

    从 GHC 7.8 开始,您可以使用 PatternSynonyms 导出独立于构造函数的模式。所以类似于@Lambdageek 的答案是

    {-# 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
    

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

    所以它看起来像普通的构造函数。

    如果你尝试使用Bar 来构造一个值,你会得到

    Main.hs:9:32:
        Bar used in an expression, but it's a non-bidirectional pattern synonym
        In the expression: Bar y
    

    【讨论】:

      【解决方案3】:

      你不能。但是,如果您的类型 T 只有合理数量的构造函数,您可能希望隐藏构造函数,而是提供一个函数,以与 maybe :: b -&gt; (a -&gt; b) -&gt; Maybe a -&gt; b 相同的精神进行模式匹配。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-04
        • 1970-01-01
        • 2018-06-09
        • 1970-01-01
        • 2012-04-29
        • 1970-01-01
        • 2021-12-31
        • 2020-07-30
        相关资源
        最近更新 更多