在Haskell中自定义concat(++)运算符

5

在Haskell中,是否可以为自定义数据类型定义自己的++运算符?

我的代码如下:

data MyType = MyType [String]

我想定义自己的连接运算符,如下所示:

instance ? MyType where
    (MyType x) ++ (MyType y) = MyType (x ++ y)

我似乎找不到实例类的名称。

3个回答

13

如果您不坚持称呼运算符(++)

import Data.Monoid

instance Monoid MyType where
    (MyType x) `mappend` (MyType y) = MyType (x ++ y)
    mempty = MyType []

然后你可以使用

(<>) :: Monoid m => m -> m -> m

这是 mappend 的别名(我以为它已经是类型类成员了,但事实并非如此 :/)。列表拥有一个 Monoid 实例,其中 mappend(++),所以这将会实现你想要的功能。 Monoid 实例还提供了

mconcat :: Monoid m => [m] -> m
你可以使用它来连接一系列MyType

这就是专业程序员和业余程序员之间的区别 - 知道有 Monoid 类型类。 - epsilonhalbe
<>运算符在列表中的定义是否与++运算符相同? - Wesley Tansey
@WesleyTansey 是的,但我刚刚查了一下,似乎在已发布的GHC版本中还不是一个类成员,所以我必须稍微修改我的解决方案。 - Daniel Fischer
2
值得注意的是,如果您有一个关联(<>)但没有mempty,那么您的类型可能是semigroup。不幸的是,Semigroup目前不是Monoid的超类。 - shachaf

4
最简单的方法就是这样做。
import Prelude hiding ((++))
import qualified Prelude as P

data MyType = MyType [String]

class PlusAble a where
    infixr 5 ++
    (++) :: a -> a -> a

instance PlusAble MyType where
    (MyType x) ++ (MyType y) = MyType (x P.++ y)

-- EDIT:
instance PlusAble [a] where
    x ++ y = x P.++ y

这个解决方案似乎会导致所有其他++操作变得模糊不清。 - Wesley Tansey
我认为你也必须为列表添加一个实例 - 我在编辑中添加了这个。 - epsilonhalbe

3

(++) 运算符不属于任何类型类。你可以轻松地检查这个问题:

$ ghci
Prelude> :info (++)
(++) :: [a] -> [a] -> [a]   -- Defined in `GHC.Base'
infixr 5 ++

所以,这只是在 GHC.Base 模块中定义的简单函数。您可以隐藏它并定义自己的函数:

import Prelude hiding ((++))
import qualified Prelude -- to get hidden (++) as Prelude.(++)

-- your brand new (++)
infixr 5 ++
(MyType x) ++ (MyType y) = MyType (x Prelude.++ y)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接