简体   繁体   English

模式匹配与构造函数

[英]Pattern matching vs. constructors

I have two almost equal functions for different data constructors and would like to know if there is a possibility to unify both. 对于不同的数据构造函数,我有两个几乎相同的函数,并想知道是否有可能统一两者。 A minimalistic example would be the following 一个简约的例子如下

f_maybe :: Maybe a -> a -> a
f_maybe (Just x) _ = x
f_maybe _        x = x

and

data T a = T1 a | T2 Int | T3

f_t :: T a -> a -> a
f_t  (T1 x) _ = x
f_t  _      x = x

Is the a way to define only one function parametrized by the type constructors (Maybe or T) and the data constructor (Just or T1) doing both? 是一种只定义一个由类型构造函数(Maybe或T)和数据构造函数(Just或T1)执行参数化的函数的方法吗?

Once you implemented Foldable instance for your data type, this idea can be expressed as foldr const . 一旦为数据类型实现了Foldable实例,这个想法就可以表示为foldr const

data T a = T1 a | T2 Int | T3

instance Foldable T where
    -- if it's T1, it has a value
    foldMap f (T1 a) = f a
    -- if it's T2 or T3, it's considered "empty"
    foldMap f (T2 _) = mempty
    foldMap f T3     = mempty

You can even let generic-deriving package derive the above Foldable instance for you: 您甚至可以让generic-deriving包为您导出上面的Foldable实例:

{-# LANGUAGE DeriveGeneric #-}

import           Generics.Deriving.Foldable
import           GHC.Generics

data T a = T1 a | T2 Int | T3
    deriving (Generic1)

instance Foldable T where
    foldMap = gfoldMapdefault

(In your case the instance is not ambiguous since only one of the constructors may contain exactly one value of type a . if the data type gets over-complicated with multiple constructors containing one or more a , you'd better do it manually or check carefully what that package derived) (在您的情况下,实例不是模糊的,因为只有一个构造函数可能只包含一个类型a值。如果数据类型过于复杂,多个构造函数包含一个或多个a ,您最好手动执行或检查小心那个包来源了什么)

And some results from ghci: ghci的一些结果:

-- `Maybe` already has the instance defined by the standard library
> foldr const 2 (Just 3)
< 3
> foldr const 2 Nothing
< 2

> foldr const 2 (T1 3)
< 3
> foldr const 2 (T2 10)
< 2
> foldr const 2 T3
< 2

In order to satisfy your needs, both types need to share a common interface through a type class. 为了满足您的需求,两种类型都需要通过类型类共享一个公共接口。 It could be done like this: 可以这样做:

class MyF mf where
    f :: mf a -> a -> a

instance MyF Maybe where
    f (Just x) _ = x
    f _        x = x

instance MyF T where
    f (T1 x) _ = x
    f _      x = x

Examples: 例子:

f (Just 3) 3
3

f Nothing "Hello"
"Hello"

f (T1 5) 3
5

f (T2 20) 2
2

You can write a unified function that has any behavior you want and works on any type of argument, but you cannot pattern match without concrete data constructor (polymorphicly), AFAIK. 您可以编写一个统一的函数,它具有您想要的任何行为并适用于任何类型的参数,但如果没有具体的数据构造函数(多态),AFAIK,您就无法进行模式匹配。


Ask yourself what type signature of this unified function would look like? 问问自己这个统一功能的类型签名是什么样的? The type signature represents idea behind function. 类型签名代表功能背后的想法。 What do Maybe and T have in common in this case so that you what to unify functions that work on them? 在这种情况下, MaybeT有什么共同之处,以便你能统一起作用于它们的函数? What kind of abstraction are you trying to build? 你想要构建什么样的抽象?

All these things are not clear from your question. 所有这些事情都不清楚你的问题。 If your idea is to just cut boilerplate (and this is not exactly boilerplate ), then do not try to unify things that are different just to make code shorter, you will get something confusing in the end. 如果你的想法只是削减样板(这不是样板 ),那么不要试图统一不同的东西只是为了缩短代码,你最终会得到一些令人困惑的东西。

Types actually should be different and functions should reject arguments of wrong type, not work on anything that looks remotely similar. 类型实际上应该是不同的,函数应该拒绝错误类型的参数,而不是在看起来类似的任何东西上工作。 Too polymorphic code is often not a good idea, because wrong code may happen to have some meaning for type checker and then you will get program that compiles, but does wrong things. 太多态的代码通常不是一个好主意,因为错误的代码可能碰巧对类型检查器有一些意义,然后你会得到编译的程序,但做错了。

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

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