我看到过以下类型的定义以及相应的Monoid
实例:
data Foo where
FooEmpty :: String -> Foo
FooAppend :: Foo -> Foo -> Foo
-- | Create a 'Foo' with a specific 'String'.
foo :: String -> Foo
foo = FooEmpty
instance Monoid Foo where
mempty :: Foo
mempty = FooEmpty ""
mappend :: Foo -> Foo -> Foo
mappend = FooAppend
你可以在Github上的gist中找到完整的代码。
这是如何使用Foo
的:
exampleFoo :: Foo
exampleFoo =
(foo "hello" <> foo " reallylongstringthatislong") <>
(foo " world" <> mempty)
< p > < code > exampleFoo 最终会变成以下这样的树形结构:
FooAppend
(FooAppend
(FooEmpty "hello")
(FooEmpty " reallylongstringthatislong"))
(FooAppend
(FooEmpty " world")
(FooEmpty ""))
Foo
可以用于将 Monoid
操作 (mempty
和 mappend
) 的序列转换为 AST。然后,可以将该 AST 解释为其他某个 Monoid
。
例如,下面是将 Foo
翻译为字符串的示例,确保字符串附加将以最佳方式发生:
fooInterp :: Foo -> String
fooInterp = go ""
where
go :: String -> Foo -> String
go accum (FooEmpty str) = str ++ accum
go accum (FooAppend foo1 foo2) = go (go accum foo2) foo1
这真的很好。方便的是我们可以确保
String
附加会按正确顺序发生。我们不必担心左相关的mappend
s。然而,让我担心的是
Foo
的Monoid
实例不是合法的Monoid
实例。例如,考虑第一个
Monoid
定律:mappend mempty x = x
如果我们将
x
设置为FooEmpty "hello"
,则会得到以下结果:mappend mempty (FooEmpty "hello") = FooEmpty "hello"
mappend (FooEmpty "") (FooEmpty "hello") = FooEmpty "hello" -- replace mempty with its def
FooAppend (FooEmpty "") (FooEmpty "hello") = FooEmpty "hello" -- replace mappend with its def
你可以看到,
FooAppend(FooEmpty "")(FooEmpty“ hello”)
并不等于FooEmpty“ hello”
。由于类似的原因,其他Monoid
定律也不成立。Haskellers通常反对非法实例。但我觉得这是一个特殊情况。我们只是试图建立一个可以解释为另一个
Monoid
结构的结构。在Foo
的情况下,我们可以确保fooInterp
函数中String
的Monoid
定律成立。
- 在构建AST时使用这些非法实例是否可行?
- 使用这些非法实例时需要注意哪些具体问题?
- 是否有替代方法来编写使用类似
Foo
的代码? 一些启用解释一个单调结构的方法而不是直接在类型上使用mappend
的方法?
Foo
类型泛化为任何Monoid
。也可以为任何类型类编写像Foo
这样的类型。 - illaboutMonoid
中实现O(1)
列表附加(适用于字符串),可以查看DList
。 - Alecdata Foo = FooEmpty | FooString String | FooAppend Foo Foo
来使Foo
合法,然后定义foo :: String -> Foo; foo = FooString
,再实现instance Monoid Foo where { mempty = FooEmpty; mappend FooEmpty m = m ; mappend m FooEmpty = m ; mappend m m' = FooAppend m m' }
。如果你不想添加另一个构造函数,你可以通过将输入与mappend
的FooEmpty ""
进行比较来达到相同的效果,但是语义有些不同。 - rampion