简体   繁体   English

在Haskell模块中是否可以导出构造函数以进行模式匹配,但不能用于构造?

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

A vanilla data type in Haskell has zero or more constructors, each of which plays two roles. Haskell中的vanilla数据类型具有零个或多个构造函数,每个构造函数都扮演两个角色。

In expressions, it supports introduction, its a function from zero or more arguments to the data type. 在表达式中,它支持引入,它是从零个或多个参数到数据类型的函数。

In patterns, it supports elimination, its kinda like a function from the data type to Maybe (tuple of argument types). 在模式中,它支持消除,它有点像从数据类型到Maybe(参数类型的元组)的函数。

Is it possible for a module signature to hide the former while exposing the latter? 模块签名是否有可能在暴露后者时隐藏前者?

The use case is this: I have a type, T, whose constructors types alone can sometimes be used to construct nonsense. 用例是这样的:我有一个类型T,它的构造函数类型有时可以用来构造废话。 I have construction functions which can be used to build instances of the type that are guaranteed not to be nonsense. 我有构造函数,可用于构建保证不是废话的类型的实例。 It would make sense to hide the constructors in this case, but it would still be useful for callers to be able to pattern match over the guaranteed-non-nonsense that they build with the construction functions. 在这种情况下隐藏构造函数是有意义的,但是对于调用者来说,能够通过构造函数构建的保证非废话模式匹配仍然是有用的。

I suspect this is impossible, but in case anyone has a way to do it, I though I would ask. 我怀疑这是不可能的,但万一有人有办法做到这一点,我会问。

Next best thing is to hide the constructors and create a bunch of functions from T -> Maybe (This, That), T -> Maybe (The, Other, Thing), etc. 接下来最好的事情是隐藏构造函数并从T - > Maybe(This,That),T - > Maybe(The,Other,Thing)等创建一堆函数。

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 . 请注意, ThingView不是递归数据类型:所有值构造函数都引用回Thing So now you can export the value constructors of ThingView and keep Thing abstract. 所以现在你可以导出ThingView的值构造ThingView并保持Thing抽象。

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 . 箭头符号的东西是GHC的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: 当然你不需要使用视图模式,你可以手工完成所有的desugaring:

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 实际上我们可以做得更好一点:没有理由复制ThingThingView所有值构造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 . 继续使用它与以前一样,但现在模式匹配可以使用FooBar

{-# LANGUAGE ViewPatterns #-}
module Main where

import ThingModule

doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
doSomethingWithThing(view -> Bar y) = y

From GHC 7.8 on you can use PatternSynonyms to export patterns independent from constructors. 从GHC 7.8开始,您可以使用PatternSynonyms来导出独立于构造函数的模式。 So an analogue to @Lambdageek's answer would be 所以类似@ 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

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 如果你试图使用Bar来构造一个值,那么你就得到了

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

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 . 但是如果你的类型T只有合理数量的构造函数,你可能想要隐藏构造函数,而是提供一个函数,它以与maybe :: b -> (a -> b) -> Maybe a -> b相同的精神进行模式匹配maybe :: b -> (a -> b) -> Maybe a -> b

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM